Get up and running with the Firefly Framework Callbacks Library in 10 minutes
- Prerequisites
- Installation
- Configuration
- Running the Application
- Your First Callback
- Testing the Setup
- Common Use Cases
- Troubleshooting
- Next Steps
Before you begin, ensure you have the following installed:
-
Java 21 or higher
java -version # Should show: openjdk version "21" or higher -
Maven 3.8+
mvn -version # Should show: Apache Maven 3.8.x or higher -
PostgreSQL 14+
psql --version # Should show: psql (PostgreSQL) 14.x or higher -
Apache Kafka 3.0+ (or Confluent Platform 7.5+)
kafka-topics --version # Should show: 3.0.x or higher
-
Docker & Docker Compose - For running dependencies easily
docker --version docker-compose --version
-
curl or Postman - For testing API endpoints
-
jq - For pretty-printing JSON responses
brew install jq # macOS sudo apt-get install jq # Ubuntu/Debian
This is the fastest way to get started with all dependencies.
git clone https://github.com/firefly-oss/fireflyframework-callbacks.git
cd fireflyframework-callbacks# Start PostgreSQL and Kafka
docker-compose up -d postgres kafka zookeeper
# Verify containers are running
docker-compose psExpected output:
NAME STATUS PORTS
postgres Up 10 seconds 0.0.0.0:5432->5432/tcp
kafka Up 10 seconds 0.0.0.0:9092->9092/tcp
zookeeper Up 10 seconds 0.0.0.0:2181->2181/tcp
mvn clean installcd fireflyframework-callbacks-web
mvn spring-boot:runIf you prefer to install dependencies manually:
macOS (Homebrew):
brew install postgresql@14
brew services start postgresql@14Ubuntu/Debian:
sudo apt-get update
sudo apt-get install postgresql-14
sudo systemctl start postgresqlCreate Database:
psql -U postgres
CREATE DATABASE callbacks_db;
CREATE USER firefly WITH PASSWORD 'firefly';
GRANT ALL PRIVILEGES ON DATABASE callbacks_db TO firefly;
\qmacOS (Homebrew):
brew install kafka
brew services start zookeeper
brew services start kafkaManual Installation:
# Download Kafka
wget https://downloads.apache.org/kafka/3.6.0/kafka_2.13-3.6.0.tgz
tar -xzf kafka_2.13-3.6.0.tgz
cd kafka_2.13-3.6.0
# Start Zookeeper
bin/zookeeper-server-start.sh config/zookeeper.properties &
# Start Kafka
bin/kafka-server-start.sh config/server.properties &git clone https://github.com/firefly-oss/fireflyframework-callbacks.git
cd fireflyframework-callbacks
mvn clean installcd fireflyframework-callbacks-web
mvn spring-boot:runCreate a .env file in the project root (or export these variables):
# Database Configuration
export DB_HOST=localhost
export DB_PORT=5432
export DB_NAME=callbacks_db
export DB_USERNAME=firefly
export DB_PASSWORD=firefly
# Kafka Configuration
export KAFKA_BOOTSTRAP_SERVERS=localhost:9092
export KAFKA_CONSUMER_GROUP=callbacks-mgmt-consumer
# Server Configuration
export SERVER_PORT=8080
# Logging
export LOG_LEVEL=INFOThe application will automatically:
- Connect to PostgreSQL using R2DBC
- Run Flyway migrations to create tables
- Connect to Kafka for event consumption
- Start the REST API on port 8080
cd fireflyframework-callbacks-web
mvn spring-boot:runcurl http://localhost:8080/actuator/health | jqExpected response:
{
"status": "UP",
"components": {
"db": {
"status": "UP"
},
"diskSpace": {
"status": "UP"
},
"ping": {
"status": "UP"
}
}
}Open your browser and navigate to:
http://localhost:8080/swagger-ui.html
You should see the interactive API documentation.
curl http://localhost:8080/actuator/info | jqLet's create a complete end-to-end callback flow in 5 steps.
Before creating callbacks, you must authorize the target domain.
curl -X POST http://localhost:8080/api/v1/authorized-domains \
-H "Content-Type: application/json" \
-d '{
"domain": "webhook.site",
"organization": "Test Organization",
"verified": true,
"active": true,
"requireHttps": true
}' | jqResponse:
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"domain": "webhook.site",
"organization": "Test Organization",
"verified": true,
"active": true,
"requireHttps": true,
"createdAt": "2025-01-15T10:00:00Z"
}💡 Tip: Use webhook.site to get a free test webhook URL.
Subscribe to a Kafka topic to receive events.
curl -X POST http://localhost:8080/api/v1/event-subscriptions \
-H "Content-Type: application/json" \
-d '{
"name": "Customer Events Subscription",
"description": "Subscribe to all customer events",
"messagingSystemType": "KAFKA",
"connectionConfig": {
"bootstrap.servers": "localhost:9092",
"group.id": "callbacks-consumer"
},
"topicOrQueue": "customer.events",
"consumerGroupId": "callbacks-consumer",
"eventTypePatterns": ["customer.*"],
"active": true
}' | jqResponse:
{
"id": "234e5678-e89b-12d3-a456-426614174001",
"name": "Customer Events Subscription",
"description": "Subscribe to all customer events",
"messagingSystemType": "KAFKA",
"connectionConfig": {
"bootstrap.servers": "localhost:9092",
"group.id": "callbacks-consumer"
},
"topicOrQueue": "customer.events",
"consumerGroupId": "callbacks-consumer",
"eventTypePatterns": ["customer.*"],
"active": true,
"maxConcurrentConsumers": 1,
"pollingIntervalMs": 1000,
"totalMessagesReceived": 0,
"totalMessagesFailed": 0,
"createdAt": "2025-01-15T10:01:00Z"
}Create a webhook that will be called when events are received.
# First, get a test webhook URL from https://webhook.site
# Copy the unique URL (e.g., https://webhook.site/abc123)
curl -X POST http://localhost:8080/api/v1/callback-configurations \
-H "Content-Type: application/json" \
-d '{
"name": "Customer CRM Webhook",
"description": "Send customer events to CRM",
"url": "https://webhook.site/YOUR-UNIQUE-ID",
"httpMethod": "POST",
"subscribedEventTypes": ["customer.created", "customer.updated"],
"status": "ACTIVE",
"signatureEnabled": true,
"secret": "my-secret-key",
"maxRetries": 3,
"retryDelayMs": 1000,
"retryBackoffMultiplier": 2.0,
"timeoutMs": 30000,
"active": true
}' | jqResponse:
{
"id": "345e6789-e89b-12d3-a456-426614174002",
"name": "Customer CRM Webhook",
"description": "Send customer events to CRM",
"url": "https://webhook.site/YOUR-UNIQUE-ID",
"httpMethod": "POST",
"status": "ACTIVE",
"subscribedEventTypes": ["customer.created", "customer.updated"],
"signatureEnabled": true,
"maxRetries": 3,
"retryDelayMs": 1000,
"retryBackoffMultiplier": 2.0,
"timeoutMs": 30000,
"active": true,
"failureThreshold": 10,
"failureCount": 0,
"createdAt": "2025-01-15T10:02:00Z"
}Now let's publish a test event to Kafka to trigger the callback.
Using Kafka Console Producer:
# Create the topic if it doesn't exist
kafka-topics --create \
--bootstrap-server localhost:9092 \
--topic customer.events \
--partitions 1 \
--replication-factor 1
# Publish a test event
echo '{
"eventType": "customer.created",
"eventId": "456e7890-e89b-12d3-a456-426614174003",
"timestamp": "2025-01-15T10:03:00Z",
"payload": {
"customerId": "CUST-001",
"firstName": "John",
"lastName": "Doe",
"email": "john.doe@example.com",
"status": "ACTIVE"
}
}' | kafka-console-producer \
--bootstrap-server localhost:9092 \
--topic customer.eventsUsing kafkacat (kcat):
# Install kafkacat
brew install kcat # macOS
# Publish event
echo '{
"eventType": "customer.created",
"eventId": "456e7890-e89b-12d3-a456-426614174003",
"timestamp": "2025-01-15T10:03:00Z",
"payload": {
"customerId": "CUST-001",
"firstName": "John",
"lastName": "Doe",
"email": "john.doe@example.com"
}
}' | kcat -P -b localhost:9092 -t customer.eventsGo to your webhook.site URL in the browser. You should see the incoming request with:
Headers:
X-Event-Type: customer.created
X-Event-Id: 456e7890-e89b-12d3-a456-426614174003
X-Timestamp: 2025-01-15T10:03:00Z
X-Firefly-Signature: <base64-encoded-hmac>
Content-Type: application/json
Body:
{
"customerId": "CUST-001",
"firstName": "John",
"lastName": "Doe",
"email": "john.doe@example.com",
"status": "ACTIVE"
}curl -X POST http://localhost:8080/api/v1/callback-executions/filter \
-H "Content-Type: application/json" \
-d '{
"page": 0,
"size": 10,
"sort": ["executedAt,DESC"]
}' | jqResponse:
{
"content": [
{
"id": "567e8901-e89b-12d3-a456-426614174004",
"configurationId": "345e6789-e89b-12d3-a456-426614174002",
"eventType": "customer.created",
"sourceEventId": "456e7890-e89b-12d3-a456-426614174003",
"status": "SUCCESS",
"responseStatusCode": 200,
"attemptNumber": 0,
"requestDurationMs": 245,
"executedAt": "2025-01-15T10:03:01Z",
"completedAt": "2025-01-15T10:03:01Z"
}
],
"page": 0,
"size": 10,
"totalElements": 1,
"totalPages": 1,
"numberOfElements": 1,
"first": true,
"last": true,
"empty": false
}🎉 Congratulations! You've successfully set up your first end-to-end callback flow!
Create a callback that will fail and retry:
# Use a non-existent URL to trigger retries
curl -X POST http://localhost:8080/api/v1/callback-configurations \
-H "Content-Type: application/json" \
-d '{
"name": "Failing Webhook",
"url": "https://webhook.site/non-existent-endpoint",
"httpMethod": "POST",
"subscribedEventTypes": ["customer.created"],
"status": "ACTIVE",
"maxRetries": 3,
"retryDelayMs": 1000,
"active": true
}' | jqPublish an event and watch the retries in the logs:
# Watch application logs
tail -f fireflyframework-callbacks-web/target/spring-boot.logPublish multiple failing events to trigger the circuit breaker:
# Publish 20 events quickly
for i in {1..20}; do
echo "{\"eventType\":\"customer.created\",\"eventId\":\"$i\",\"payload\":{}}" | \
kafka-console-producer --bootstrap-server localhost:9092 --topic customer.events
doneCheck circuit breaker metrics:
curl http://localhost:8080/actuator/metrics/resilience4j.circuitbreaker.state | jqVerify the HMAC signature on the receiving end:
Node.js Example:
const crypto = require('crypto');
function verifySignature(payload, signature, secret) {
const hmac = crypto.createHmac('sha256', secret);
hmac.update(JSON.stringify(payload));
const expectedSignature = hmac.digest('base64');
return signature === expectedSignature;
}
// In your webhook handler
app.post('/webhook', (req, res) => {
const signature = req.headers['x-firefly-signature'];
const payload = req.body;
const secret = 'my-secret-key';
if (verifySignature(payload, signature, secret)) {
console.log('✅ Signature verified');
res.status(200).send('OK');
} else {
console.log('❌ Invalid signature');
res.status(401).send('Unauthorized');
}
});Send customer lifecycle events to Salesforce or HubSpot.
# 1. Authorize CRM domain
curl -X POST http://localhost:8080/api/v1/authorized-domains \
-H "Content-Type: application/json" \
-d '{
"domain": "api.salesforce.com",
"organization": "Salesforce",
"verified": true,
"active": true
}' | jq
# 2. Create callback for customer events
curl -X POST http://localhost:8080/api/v1/callback-configurations \
-H "Content-Type: application/json" \
-d '{
"name": "Salesforce Customer Sync",
"url": "https://api.salesforce.com/webhooks/firefly",
"httpMethod": "POST",
"subscribedEventTypes": ["customer.created", "customer.updated", "customer.deleted"],
"status": "ACTIVE",
"signatureEnabled": true,
"secret": "salesforce-webhook-secret",
"active": true
}' | jqStream events to Segment or custom analytics platform.
# 1. Authorize analytics domain
curl -X POST http://localhost:8080/api/v1/authorized-domains \
-H "Content-Type: application/json" \
-d '{
"domain": "api.segment.io",
"organization": "Segment",
"verified": true,
"active": true
}' | jq
# 2. Create callback for all events
curl -X POST http://localhost:8080/api/v1/callback-configurations \
-H "Content-Type: application/json" \
-d '{
"name": "Segment Analytics",
"url": "https://api.segment.io/v1/track",
"httpMethod": "POST",
"subscribedEventTypes": ["*"],
"status": "ACTIVE",
"customHeaders": {
"Authorization": "Bearer YOUR_SEGMENT_KEY"
},
"active": true
}' | jqConfigure different callbacks per tenant.
# Tenant A - CRM Integration
curl -X POST http://localhost:8080/api/v1/callback-configurations \
-H "Content-Type: application/json" \
-d '{
"name": "Tenant A - CRM",
"url": "https://tenant-a.crm.com/webhooks",
"subscribedEventTypes": ["customer.*"],
"tenantId": "tenant-a",
"active": true
}' | jq
# Tenant B - Analytics
curl -X POST http://localhost:8080/api/v1/callback-configurations \
-H "Content-Type: application/json" \
-d '{
"name": "Tenant B - Analytics",
"url": "https://tenant-b.analytics.com/events",
"subscribedEventTypes": ["*"],
"tenantId": "tenant-b",
"active": true
}' | jqError: Connection refused: localhost:5432
Solution: Ensure PostgreSQL is running
# Check if PostgreSQL is running
docker-compose ps postgres
# or
brew services list | grep postgresql
# Start PostgreSQL
docker-compose up -d postgres
# or
brew services start postgresql@14Error: Connection refused: localhost:9092
Solution: Ensure Kafka is running
# Check if Kafka is running
docker-compose ps kafka
# or
brew services list | grep kafka
# Start Kafka
docker-compose up -d kafka
# or
brew services start kafkaPossible Causes:
-
Domain not authorized
# Check authorized domains curl -X POST http://localhost:8080/api/v1/authorized-domains/filter \ -H "Content-Type: application/json" \ -d '{"page": 0, "size": 100}' | jq
-
Event subscription not active
# Check subscriptions curl -X POST http://localhost:8080/api/v1/event-subscriptions/filter \ -H "Content-Type: application/json" \ -d '{"page": 0, "size": 100}' | jq
-
Event type mismatch
- Ensure
subscribedEventTypesin callback config matches theeventTypein the Kafka message
- Ensure
-
Circuit breaker is OPEN
# Check circuit breaker state curl http://localhost:8080/actuator/health | jq '.components.circuitBreakers'
Error: Flyway migration failed
Solution: Reset the database
# Drop and recreate database
psql -U postgres
DROP DATABASE callbacks_db;
CREATE DATABASE callbacks_db;
GRANT ALL PRIVILEGES ON DATABASE callbacks_db TO firefly;
\q
# Restart application (migrations will run automatically)
mvn spring-boot:runSolution: Adjust JVM heap size
export MAVEN_OPTS="-Xmx512m -Xms256m"
mvn spring-boot:runexport LOG_LEVEL=DEBUG
export LOG_LEVEL_R2DBC=DEBUG
mvn spring-boot:runNow that you have the basics working, explore these advanced topics:
Read the Architecture Deep Dive to understand:
- Event flow and processing
- Circuit breaker patterns
- Security architecture
- Performance optimizations
See the Testing Guide for:
- Unit testing strategies
- Integration testing with Testcontainers
- End-to-end testing examples
- Configure production database with connection pooling
- Set up Kafka cluster with replication
- Enable Prometheus metrics
- Configure log aggregation (ELK, Splunk)
- Set up alerting for circuit breaker states
- Implement custom filter expressions (JSONPath)
- Configure IP whitelisting for domains
- Set up rate limiting per domain
- Implement custom retry strategies
# Prometheus metrics
curl http://localhost:8080/actuator/prometheus
# Circuit breaker metrics
curl http://localhost:8080/actuator/metrics/resilience4j.circuitbreaker.state
# HTTP client metrics
curl http://localhost:8080/actuator/metrics/http.client.requestsNeed Help?
© 2025 Firefly Software Foundation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Happy Coding! 🚀