Fallbacks
Fallbacks in Ductape provide automatic failover between multiple providers or actions. When one provider fails, Ductape automatically tries the next option in your fallback chain until one succeeds or all options are exhausted.
What is a Fallback?
A fallback is a resilience mechanism that:
- Defines multiple provider/action options in priority order
- Automatically switches to the next option when one fails
- Tracks provider health and availability
- Supports retry logic per provider
- Can integrate with healthchecks for smart routing
- Maintains historical performance data
Use fallbacks for:
- Provider redundancy - Switch between payment gateways, SMS providers, etc.
- High availability - Ensure critical services stay operational
- Cost optimization - Route to cheaper providers when primary is down
- Geographic routing - Failover to regional providers
- API resilience - Handle third-party API outages gracefully
Creating a Fallback
Create a fallback using ductape.fallback.create():
- TypeScript
- Java
- Go
- .NET
import Ductape from '@ductape/sdk';
import { FeatureEventTypes, DataTypes } from '@ductape/sdk/types';
const ductape = new Ductape({
accessKey: 'your-access-key',
});
await ductape.fallback.create({
name: 'Payment Provider Fallback',
tag: 'payment-fallback',
description: 'Stripe to PayPal to Square fallback chain',
input: {
amount: { type: DataTypes.NUMBER },
currency: { type: DataTypes.STRING },
customerId: { type: DataTypes.STRING },
},
options: [
{
type: FeatureEventTypes.ACTION,
event: 'create-charge',
app: 'stripe',
input: {
amount: '$Input{amount}',
currency: '$Input{currency}',
customer: '$Input{customerId}',
},
output: {
transactionId: '$Response{id}',
status: '$Response{status}',
},
retries: 2,
healthcheck: 'stripe-health', // Optional: use healthcheck status
},
{
type: FeatureEventTypes.ACTION,
event: 'create-payment',
app: 'paypal',
input: {
amount: '$Input{amount}',
currency: '$Input{currency}',
},
output: {
transactionId: '$Response{payment_id}',
status: '$Response{state}',
},
retries: 2,
healthcheck: 'paypal-health',
},
{
type: FeatureEventTypes.ACTION,
event: 'charge',
app: 'square',
input: {
amount_money: {
amount: '$Input{amount}',
currency: '$Input{currency}',
},
},
output: {
transactionId: '$Response{payment_id}',
status: '$Response{status}',
},
retries: 1,
},
],
});
import app.ductape.sdk.Ductape;
import app.ductape.sdk.core.EnvType;
import app.ductape.sdk.core.RequestContext;
import Map.of( FeatureEventTypes, DataTypes ) from '@ductape/sdk/types';
RequestContext auth = new RequestContext(null, null, null, null, 'your-access-key');
Ductape ductape = new Ductape(EnvType.PRODUCTION, auth);
ductape.fallback.create(Map.of(
"name", "Payment Provider Fallback",
"tag", "payment-fallback",
"description", "Stripe to PayPal to Square fallback chain",
input: Map.of(
amount: Map.of( type: DataTypes.NUMBER ),
currency: Map.of( type: DataTypes.STRING ),
customerId: Map.of( type: DataTypes.STRING ),
),
options: [
Map.of(
type: FeatureEventTypes.ACTION,
"event", "create-charge",
"app", "stripe",
input: Map.of(
"amount", "$InputMap.of(amount)",
"currency", "$InputMap.of(currency)",
"customer", "$InputMap.of(customerId)",
),
output: Map.of(
"transactionId", "$ResponseMap.of(id)",
"status", "$ResponseMap.of(status)",
),
"retries", 2,
"healthcheck", "stripe-health", // Optional: use healthcheck status
),
Map.of(
type: FeatureEventTypes.ACTION,
"event", "create-payment",
"app", "paypal",
input: Map.of(
"amount", "$InputMap.of(amount)",
"currency", "$InputMap.of(currency)",
),
output: Map.of(
"transactionId", "$ResponseMap.of(payment_id)",
"status", "$ResponseMap.of(state)",
),
"retries", 2,
"healthcheck", "paypal-health",
),
Map.of(
type: FeatureEventTypes.ACTION,
"event", "charge",
"app", "square",
input: Map.of(
amount_money: Map.of(
"amount", "$InputMap.of(amount)",
"currency", "$InputMap.of(currency)",
),
),
output: Map.of(
"transactionId", "$ResponseMap.of(payment_id)",
"status", "$ResponseMap.of(status)",
),
"retries", 1,
),
],
));
import (
"context"
"github.com/ductape/ductape/sdk/go/core"
"github.com/ductape/ductape/sdk/go/ductape"
)
import { FeatureEventTypes, DataTypes } from '@ductape/sdk/types';
auth := core.NewRequestContext("", "", "", "", 'your-access-key')
client, err := client.New(core.EnvProduction, auth)
if err != nil {
return err
}
client.fallback.create({
"name": "Payment Provider Fallback",
"tag": "payment-fallback",
"description": "Stripe to PayPal to Square fallback chain",
input: {
amount: { type: DataTypes.NUMBER },
currency: { type: DataTypes.STRING },
customerId: { type: DataTypes.STRING },
},
options: [
{
type: FeatureEventTypes.ACTION,
"event": "create-charge",
"app": "stripe",
input: {
"amount": "$Input{amount}",
"currency": "$Input{currency}",
"customer": "$Input{customerId}",
},
output: {
"transactionId": "$Response{id}",
"status": "$Response{status}",
},
"retries": 2,
"healthcheck": "stripe-health", // Optional: use healthcheck status
},
{
type: FeatureEventTypes.ACTION,
"event": "create-payment",
"app": "paypal",
input: {
"amount": "$Input{amount}",
"currency": "$Input{currency}",
},
output: {
"transactionId": "$Response{payment_id}",
"status": "$Response{state}",
},
"retries": 2,
"healthcheck": "paypal-health",
},
{
type: FeatureEventTypes.ACTION,
"event": "charge",
"app": "square",
input: {
amount_money: {
"amount": "$Input{amount}",
"currency": "$Input{currency}",
},
},
output: {
"transactionId": "$Response{payment_id}",
"status": "$Response{status}",
},
"retries": 1,
},
],
});
using Ductape.Sdk;
using Ductape.Sdk.Core;
import { FeatureEventTypes, DataTypes } from '@ductape/sdk/types';
var auth = new RequestContext(null, null, null, null, 'your-access-key', null);
var ductape = new Ductape(EnvType.Production, auth);
await ductape.fallback.create({
["name"] = "Payment Provider Fallback",
["tag"] = "payment-fallback",
["description"] = "Stripe to PayPal to Square fallback chain",
input: {
amount: { type: DataTypes.NUMBER },
currency: { type: DataTypes.STRING },
customerId: { type: DataTypes.STRING },
},
options: [
{
type: FeatureEventTypes.ACTION,
["event"] = "create-charge",
["app"] = "stripe",
input: {
["amount"] = "$Input{amount}",
["currency"] = "$Input{currency}",
["customer"] = "$Input{customerId}",
},
output: {
["transactionId"] = "$Response{id}",
["status"] = "$Response{status}",
},
["retries"] = 2,
["healthcheck"] = "stripe-health", // Optional: use healthcheck status
},
{
type: FeatureEventTypes.ACTION,
["event"] = "create-payment",
["app"] = "paypal",
input: {
["amount"] = "$Input{amount}",
["currency"] = "$Input{currency}",
},
output: {
["transactionId"] = "$Response{payment_id}",
["status"] = "$Response{state}",
},
["retries"] = 2,
["healthcheck"] = "paypal-health",
},
{
type: FeatureEventTypes.ACTION,
["event"] = "charge",
["app"] = "square",
input: {
amount_money: {
["amount"] = "$Input{amount}",
["currency"] = "$Input{currency}",
},
},
output: {
["transactionId"] = "$Response{payment_id}",
["status"] = "$Response{status}",
},
["retries"] = 1,
},
],
});
Fallback Fields
| Field | Type | Description |
|---|---|---|
name | string | Display name for the fallback |
tag | string | Unique identifier |
description | string | Description of the fallback purpose |
input | Record<string, IFeatureInput> | Input schema definition |
options | IFallbackOptions[] | Array of provider options in priority order |
Fallback Option Fields
Each option in the options array has:
| Field | Type | Description |
|---|---|---|
type | FeatureEventTypes | Type of action (ACTION or FEATURE) |
event | string | Tag of the action/feature to execute |
app | string | App tag (required if type is ACTION) |
input | object | Input mapping for the action |
output | object | Output mapping from the action response |
retries | number | Number of retry attempts before failing over |
healthcheck | string | Optional healthcheck tag for provider status |
provider_status | string | Provider availability status (tracked automatically) |
last_available | boolean | Whether provider was available last check (tracked automatically) |
last_checked | Date | Last healthcheck timestamp (tracked automatically) |
check_interval | number | Healthcheck interval in milliseconds (optional) |
Running a Fallback
Execute fallback logic at runtime using ductape.fallback.run():
- TypeScript
- Java
- Go
- .NET
const result = await ductape.fallback.run({
tag: 'payment-fallback',
input: {
amount: 2500,
currency: 'USD',
customerId: 'cus_123',
},
session: {
tag: 'user-session',
token: 'session-token',
},
cache: 'payment-cache', // Optional: cache results
});
console.log('Payment result:', result);
console.log('Provider used:', result.provider);
console.log('Transaction ID:', result.transactionId);
Map<String, Object> result = ductape.fallback.run(Map.of(
"tag", "payment-fallback",
input: Map.of(
"amount", 2500,
"currency", "USD",
"customerId", "cus_123",
),
session: Map.of(
"tag", "user-session",
"token", "session-token",
),
"cache", "payment-cache", // Optional: cache results
));
System.out.println('Payment "result", ", result);
System.out.println("Provider "used", ", result.provider);
System.out.println("Transaction ID:', result.transactionId);
result := client.fallback.run({
"tag": "payment-fallback",
input: {
"amount": 2500,
"currency": "USD",
"customerId": "cus_123",
},
session: {
"tag": "user-session",
"token": "session-token",
},
"cache": "payment-cache", // Optional: cache results
});
fmt.Println('Payment "result": ", result);
fmt.Println("Provider "used": ", result.provider);
fmt.Println("Transaction ID:', result.transactionId);
var result = await ductape.fallback.run({
["tag"] = "payment-fallback",
input: {
["amount"] = 2500,
["currency"] = "USD",
["customerId"] = "cus_123",
},
session: {
["tag"] = "user-session",
["token"] = "session-token",
},
["cache"] = "payment-cache", // Optional: cache results
});
Console.WriteLine('Payment ["result"] = ", result);
Console.WriteLine("Provider ["used"] = ", result.provider);
Console.WriteLine("Transaction ID:', result.transactionId);
Run Parameters
| Parameter | Type | Description |
|---|---|---|
env | string | Environment (e.g., 'dev', 'prd') |
product | string | Product tag |
tag | string | Fallback tag to execute |
input | object | Input data for the fallback |
session | object | Optional session information |
cache | string | Optional cache tag |
How Execution Works
- Try First Option: Execute the first provider in the options array
- Retry on Failure: If it fails, retry up to the specified retry count
- Failover: If all retries fail, move to the next option
- Healthcheck Integration: Skip providers marked unhealthy (if healthchecks configured)
- Return Success: Return as soon as any provider succeeds
- All Failed: If all providers fail, return error with failure details
Fetching Fallbacks
Get All Fallbacks
- TypeScript
- Java
- Go
- .NET
const fallbacks = await ductape.fallback.fetchAll();
fallbacks.forEach(fallback => {
console.log(`${fallback.name} (${fallback.tag})`);
console.log(`Options: ${fallback.options.length}`);
fallback.options.forEach((option, i) => {
console.log(` ${i + 1}. ${option.app}/${option.event}`);
console.log(` Status: ${option.provider_status || 'unknown'}`);
console.log(` Retries: ${option.retries}`);
});
});
Map<String, Object> fallbacks = ductape.fallback.fetchAll();
fallbacks.forEach(fallback => Map.of(
System.out.println(`$Map.of(fallback.name) ($Map.of(fallback.tag))`);
System.out.println(`Options: $Map.of(fallback.options.length)`);
fallback.options.forEach((option, i) => Map.of(
System.out.println(` $Map.of(i + 1). $Map.of(option.app)/$Map.of(option.event)`);
System.out.println(` Status: $Map.of(option.provider_status || 'unknown')`);
System.out.println(` Retries: $Map.of(option.retries)`);
));
));
fallbacks := client.fallback.fetchAll();
fallbacks.forEach(fallback => {
fmt.Println(`${fallback.name} (${fallback.tag})`);
fmt.Println(`Options: ${fallback.options.length}`);
fallback.options.forEach((option, i) => {
fmt.Println(` ${i + 1}. ${option.app}/${option.event}`);
fmt.Println(` Status: ${option.provider_status || 'unknown'}`);
fmt.Println(` Retries: ${option.retries}`);
});
});
var fallbacks = await ductape.fallback.fetchAll();
fallbacks.forEach(fallback => {
Console.WriteLine(`${fallback.name} (${fallback.tag})`);
Console.WriteLine(`Options: ${fallback.options.length}`);
fallback.options.forEach((option, i) => {
Console.WriteLine(` ${i + 1}. ${option.app}/${option.event}`);
Console.WriteLine(` Status: ${option.provider_status || 'unknown'}`);
Console.WriteLine(` Retries: ${option.retries}`);
});
});
Get a Specific Fallback
- TypeScript
- Java
- Go
- .NET
const fallback = await ductape.fallback.fetch('payment-fallback');
console.log('Fallback:', fallback.name);
console.log('Description:', fallback.description);
console.log('Provider Options:');
fallback.options.forEach((option, i) => {
console.log(` ${i + 1}. ${option.app}/${option.event}`);
console.log(` Last Available: ${option.last_available}`);
console.log(` Last Checked: ${option.last_checked}`);
});
Map<String, Object> fallback = ductape.fallback.fetch('payment-fallback');
System.out.println('"Fallback", ", fallback.name);
System.out.println(""Description", ", fallback.description);
System.out.println("Provider Options:');
fallback.options.forEach((option, i) => Map.of(
System.out.println(` $Map.of(i + 1). $Map.of(option.app)/$Map.of(option.event)`);
System.out.println(` Last Available: $Map.of(option.last_available)`);
System.out.println(` Last Checked: $Map.of(option.last_checked)`);
));
fallback := client.fallback.fetch('payment-fallback');
fmt.Println('"Fallback": ", fallback.name);
fmt.Println(""Description": ", fallback.description);
fmt.Println("Provider Options:');
fallback.options.forEach((option, i) => {
fmt.Println(` ${i + 1}. ${option.app}/${option.event}`);
fmt.Println(` Last Available: ${option.last_available}`);
fmt.Println(` Last Checked: ${option.last_checked}`);
});
var fallback = await ductape.fallback.fetch('payment-fallback');
Console.WriteLine('["Fallback"] = ", fallback.name);
Console.WriteLine("["Description"] = ", fallback.description);
Console.WriteLine("Provider Options:');
fallback.options.forEach((option, i) => {
Console.WriteLine(` ${i + 1}. ${option.app}/${option.event}`);
Console.WriteLine(` Last Available: ${option.last_available}`);
Console.WriteLine(` Last Checked: ${option.last_checked}`);
});
Updating Fallbacks
Update fallback configuration using ductape.fallback.update():
- TypeScript
- Java
- Go
- .NET
await ductape.fallback.update('payment-fallback', {
description: 'Updated payment provider fallback',
options: [
{
type: FeatureEventTypes.ACTION,
event: 'create-charge',
app: 'stripe',
input: {
amount: '$Input{amount}',
currency: '$Input{currency}',
},
output: {
transactionId: '$Response{id}',
},
retries: 3, // Increased retries
},
// Add new provider
{
type: FeatureEventTypes.ACTION,
event: 'create-payment',
app: 'adyen',
input: {
amount: '$Input{amount}',
currency: '$Input{currency}',
},
output: {
transactionId: '$Response{pspReference}',
},
retries: 2,
},
],
});
ductape.fallback.update('payment-fallback', Map.of(
"description", "Updated payment provider fallback",
options: [
Map.of(
type: FeatureEventTypes.ACTION,
"event", "create-charge",
"app", "stripe",
input: Map.of(
"amount", "$InputMap.of(amount)",
"currency", "$InputMap.of(currency)",
),
output: Map.of(
"transactionId", "$ResponseMap.of(id)",
),
"retries", 3, // Increased retries
),
// Add new provider
Map.of(
type: FeatureEventTypes.ACTION,
"event", "create-payment",
"app", "adyen",
input: Map.of(
"amount", "$InputMap.of(amount)",
"currency", "$InputMap.of(currency)",
),
output: Map.of(
"transactionId", "$ResponseMap.of(pspReference)",
),
"retries", 2,
),
],
));
client.fallback.update('payment-fallback', {
"description": "Updated payment provider fallback",
options: [
{
type: FeatureEventTypes.ACTION,
"event": "create-charge",
"app": "stripe",
input: {
"amount": "$Input{amount}",
"currency": "$Input{currency}",
},
output: {
"transactionId": "$Response{id}",
},
"retries": 3, // Increased retries
},
// Add new provider
{
type: FeatureEventTypes.ACTION,
"event": "create-payment",
"app": "adyen",
input: {
"amount": "$Input{amount}",
"currency": "$Input{currency}",
},
output: {
"transactionId": "$Response{pspReference}",
},
"retries": 2,
},
],
});
await ductape.fallback.update('payment-fallback', {
["description"] = "Updated payment provider fallback",
options: [
{
type: FeatureEventTypes.ACTION,
["event"] = "create-charge",
["app"] = "stripe",
input: {
["amount"] = "$Input{amount}",
["currency"] = "$Input{currency}",
},
output: {
["transactionId"] = "$Response{id}",
},
["retries"] = 3, // Increased retries
},
// Add new provider
{
type: FeatureEventTypes.ACTION,
["event"] = "create-payment",
["app"] = "adyen",
input: {
["amount"] = "$Input{amount}",
["currency"] = "$Input{currency}",
},
output: {
["transactionId"] = "$Response{pspReference}",
},
["retries"] = 2,
},
],
});
Using Fallbacks in Features
Integrate fallbacks into feature workflows:
- TypeScript
- Java
- Go
- .NET
import { FeatureEventTypes, DataTypes } from '@ductape/sdk/types';
await ductape.features.create({
tag: 'process-payment',
name: 'Process Payment with Fallback',
input: {
amount: { type: DataTypes.NUMBER },
currency: { type: DataTypes.STRING },
userId: { type: DataTypes.STRING },
},
events: [
// Validate payment
{
type: FeatureEventTypes.ACTION,
event: 'validate-payment',
app: 'payment-service',
input: {
amount: '$Input{amount}',
userId: '$Input{userId}',
},
},
// Execute payment with fallback
{
type: FeatureEventTypes.FALLBACK,
event: 'payment-fallback',
input: {
amount: '$Input{amount}',
currency: '$Input{currency}',
},
},
// Log successful payment
{
type: FeatureEventTypes.ACTION,
event: 'log-payment',
app: 'logging-service',
input: {
transactionId: '$Sequence{payment-fallback}{transactionId}',
provider: '$Sequence{payment-fallback}{provider}',
amount: '$Input{amount}',
},
},
],
});
import Map.of( FeatureEventTypes, DataTypes ) from '@ductape/sdk/types';
ductape.features.create(Map.of(
"tag", "process-payment",
"name", "Process Payment with Fallback",
input: Map.of(
amount: Map.of( type: DataTypes.NUMBER ),
currency: Map.of( type: DataTypes.STRING ),
userId: Map.of( type: DataTypes.STRING ),
),
events: [
// Validate payment
Map.of(
type: FeatureEventTypes.ACTION,
"event", "validate-payment",
"app", "payment-service",
input: Map.of(
"amount", "$InputMap.of(amount)",
"userId", "$InputMap.of(userId)",
),
),
// Execute payment with fallback
Map.of(
type: FeatureEventTypes.FALLBACK,
"event", "payment-fallback",
input: Map.of(
"amount", "$InputMap.of(amount)",
"currency", "$InputMap.of(currency)",
),
),
// Log successful payment
Map.of(
type: FeatureEventTypes.ACTION,
"event", "log-payment",
"app", "logging-service",
input: Map.of(
"transactionId", "$SequenceMap.of(payment-fallback)Map.of(transactionId)",
"provider", "$SequenceMap.of(payment-fallback)Map.of(provider)",
"amount", "$InputMap.of(amount)",
),
),
],
));
import { FeatureEventTypes, DataTypes } from '@ductape/sdk/types';
client.features.create({
"tag": "process-payment",
"name": "Process Payment with Fallback",
input: {
amount: { type: DataTypes.NUMBER },
currency: { type: DataTypes.STRING },
userId: { type: DataTypes.STRING },
},
events: [
// Validate payment
{
type: FeatureEventTypes.ACTION,
"event": "validate-payment",
"app": "payment-service",
input: {
"amount": "$Input{amount}",
"userId": "$Input{userId}",
},
},
// Execute payment with fallback
{
type: FeatureEventTypes.FALLBACK,
"event": "payment-fallback",
input: {
"amount": "$Input{amount}",
"currency": "$Input{currency}",
},
},
// Log successful payment
{
type: FeatureEventTypes.ACTION,
"event": "log-payment",
"app": "logging-service",
input: {
"transactionId": "$Sequence{payment-fallback}{transactionId}",
"provider": "$Sequence{payment-fallback}{provider}",
"amount": "$Input{amount}",
},
},
],
});
import { FeatureEventTypes, DataTypes } from '@ductape/sdk/types';
await ductape.features.create({
["tag"] = "process-payment",
["name"] = "Process Payment with Fallback",
input: {
amount: { type: DataTypes.NUMBER },
currency: { type: DataTypes.STRING },
userId: { type: DataTypes.STRING },
},
events: [
// Validate payment
{
type: FeatureEventTypes.ACTION,
["event"] = "validate-payment",
["app"] = "payment-service",
input: {
["amount"] = "$Input{amount}",
["userId"] = "$Input{userId}",
},
},
// Execute payment with fallback
{
type: FeatureEventTypes.FALLBACK,
["event"] = "payment-fallback",
input: {
["amount"] = "$Input{amount}",
["currency"] = "$Input{currency}",
},
},
// Log successful payment
{
type: FeatureEventTypes.ACTION,
["event"] = "log-payment",
["app"] = "logging-service",
input: {
["transactionId"] = "$Sequence{payment-fallback}{transactionId}",
["provider"] = "$Sequence{payment-fallback}{provider}",
["amount"] = "$Input{amount}",
},
},
],
});
Example: SMS Provider Fallback
- TypeScript
- Java
- Go
- .NET
import Ductape from '@ductape/sdk';
import { FeatureEventTypes, DataTypes } from '@ductape/sdk/types';
const ductape = new Ductape({
accessKey: 'your-access-key',
});
await ductape.product.init('messaging-app');
// Create SMS fallback
await ductape.fallback.create({
name: 'SMS Provider Fallback',
tag: 'sms-fallback',
description: 'Twilio to Vonage to AWS SNS',
input: {
phone: { type: DataTypes.STRING },
message: { type: DataTypes.STRING },
},
options: [
{
type: FeatureEventTypes.ACTION,
event: 'send-sms',
app: 'twilio',
input: {
to: '$Input{phone}',
body: '$Input{message}',
},
output: {
messageId: '$Response{sid}',
status: '$Response{status}',
},
retries: 2,
healthcheck: 'twilio-health',
},
{
type: FeatureEventTypes.ACTION,
event: 'send-sms',
app: 'vonage',
input: {
to: '$Input{phone}',
text: '$Input{message}',
},
output: {
messageId: '$Response{message_id}',
status: '$Response{status}',
},
retries: 2,
healthcheck: 'vonage-health',
},
{
type: FeatureEventTypes.ACTION,
event: 'publish-sms',
app: 'aws-sns',
input: {
PhoneNumber: '$Input{phone}',
Message: '$Input{message}',
},
output: {
messageId: '$Response{MessageId}',
},
retries: 1,
},
],
});
// Send SMS with automatic fallback
const result = await ductape.fallback.run({
tag: 'sms-fallback',
input: {
phone: '+1234567890',
message: 'Your verification code is 123456',
},
});
console.log(`SMS sent via ${result.provider}`);
console.log(`Message ID: ${result.messageId}`);
import app.ductape.sdk.Ductape;
import app.ductape.sdk.core.EnvType;
import app.ductape.sdk.core.RequestContext;
import Map.of( FeatureEventTypes, DataTypes ) from '@ductape/sdk/types';
RequestContext auth = new RequestContext(null, null, null, null, 'your-access-key');
Ductape ductape = new Ductape(EnvType.PRODUCTION, auth);
ductape.product.init('messaging-app');
// Create SMS fallback
ductape.fallback.create(Map.of(
"name", "SMS Provider Fallback",
"tag", "sms-fallback",
"description", "Twilio to Vonage to AWS SNS",
input: Map.of(
phone: Map.of( type: DataTypes.STRING ),
message: Map.of( type: DataTypes.STRING ),
),
options: [
Map.of(
type: FeatureEventTypes.ACTION,
"event", "send-sms",
"app", "twilio",
input: Map.of(
"to", "$InputMap.of(phone)",
"body", "$InputMap.of(message)",
),
output: Map.of(
"messageId", "$ResponseMap.of(sid)",
"status", "$ResponseMap.of(status)",
),
"retries", 2,
"healthcheck", "twilio-health",
),
Map.of(
type: FeatureEventTypes.ACTION,
"event", "send-sms",
"app", "vonage",
input: Map.of(
"to", "$InputMap.of(phone)",
"text", "$InputMap.of(message)",
),
output: Map.of(
"messageId", "$ResponseMap.of(message_id)",
"status", "$ResponseMap.of(status)",
),
"retries", 2,
"healthcheck", "vonage-health",
),
Map.of(
type: FeatureEventTypes.ACTION,
"event", "publish-sms",
"app", "aws-sns",
input: Map.of(
"PhoneNumber", "$InputMap.of(phone)",
"Message", "$InputMap.of(message)",
),
output: Map.of(
"messageId", "$ResponseMap.of(MessageId)",
),
"retries", 1,
),
],
));
// Send SMS with automatic fallback
Map<String, Object> result = ductape.fallback.run(Map.of(
"tag", "sms-fallback",
input: Map.of(
"phone", "+1234567890",
"message", "Your verification code is 123456",
),
));
System.out.println(`SMS sent via $Map.of(result.provider)`);
System.out.println(`Message ID: $Map.of(result.messageId)`);
import (
"context"
"github.com/ductape/ductape/sdk/go/core"
"github.com/ductape/ductape/sdk/go/ductape"
)
import { FeatureEventTypes, DataTypes } from '@ductape/sdk/types';
auth := core.NewRequestContext("", "", "", "", 'your-access-key')
client, err := client.New(core.EnvProduction, auth)
if err != nil {
return err
}
client.product.init('messaging-app');
// Create SMS fallback
client.fallback.create({
"name": "SMS Provider Fallback",
"tag": "sms-fallback",
"description": "Twilio to Vonage to AWS SNS",
input: {
phone: { type: DataTypes.STRING },
message: { type: DataTypes.STRING },
},
options: [
{
type: FeatureEventTypes.ACTION,
"event": "send-sms",
"app": "twilio",
input: {
"to": "$Input{phone}",
"body": "$Input{message}",
},
output: {
"messageId": "$Response{sid}",
"status": "$Response{status}",
},
"retries": 2,
"healthcheck": "twilio-health",
},
{
type: FeatureEventTypes.ACTION,
"event": "send-sms",
"app": "vonage",
input: {
"to": "$Input{phone}",
"text": "$Input{message}",
},
output: {
"messageId": "$Response{message_id}",
"status": "$Response{status}",
},
"retries": 2,
"healthcheck": "vonage-health",
},
{
type: FeatureEventTypes.ACTION,
"event": "publish-sms",
"app": "aws-sns",
input: {
"PhoneNumber": "$Input{phone}",
"Message": "$Input{message}",
},
output: {
"messageId": "$Response{MessageId}",
},
"retries": 1,
},
],
});
// Send SMS with automatic fallback
result := client.fallback.run({
"tag": "sms-fallback",
input: {
"phone": "+1234567890",
"message": "Your verification code is 123456",
},
});
fmt.Println(`SMS sent via ${result.provider}`);
fmt.Println(`Message ID: ${result.messageId}`);
using Ductape.Sdk;
using Ductape.Sdk.Core;
import { FeatureEventTypes, DataTypes } from '@ductape/sdk/types';
var auth = new RequestContext(null, null, null, null, 'your-access-key', null);
var ductape = new Ductape(EnvType.Production, auth);
await ductape.product.init('messaging-app');
// Create SMS fallback
await ductape.fallback.create({
["name"] = "SMS Provider Fallback",
["tag"] = "sms-fallback",
["description"] = "Twilio to Vonage to AWS SNS",
input: {
phone: { type: DataTypes.STRING },
message: { type: DataTypes.STRING },
},
options: [
{
type: FeatureEventTypes.ACTION,
["event"] = "send-sms",
["app"] = "twilio",
input: {
["to"] = "$Input{phone}",
["body"] = "$Input{message}",
},
output: {
["messageId"] = "$Response{sid}",
["status"] = "$Response{status}",
},
["retries"] = 2,
["healthcheck"] = "twilio-health",
},
{
type: FeatureEventTypes.ACTION,
["event"] = "send-sms",
["app"] = "vonage",
input: {
["to"] = "$Input{phone}",
["text"] = "$Input{message}",
},
output: {
["messageId"] = "$Response{message_id}",
["status"] = "$Response{status}",
},
["retries"] = 2,
["healthcheck"] = "vonage-health",
},
{
type: FeatureEventTypes.ACTION,
["event"] = "publish-sms",
["app"] = "aws-sns",
input: {
["PhoneNumber"] = "$Input{phone}",
["Message"] = "$Input{message}",
},
output: {
["messageId"] = "$Response{MessageId}",
},
["retries"] = 1,
},
],
});
// Send SMS with automatic fallback
var result = await ductape.fallback.run({
["tag"] = "sms-fallback",
input: {
["phone"] = "+1234567890",
["message"] = "Your verification code is 123456",
},
});
Console.WriteLine(`SMS sent via ${result.provider}`);
Console.WriteLine(`Message ID: ${result.messageId}`);
Healthcheck Integration
Combine fallbacks with healthchecks for intelligent provider selection:
- TypeScript
- Java
- Go
- .NET
// Create healthchecks for providers
await ductape.health.create({
name: 'Stripe Health',
tag: 'stripe-health',
app: 'stripe',
event: 'ping',
interval: 60000, // Check every minute
retries: 2,
envs: [
{
slug: 'prd',
input: {},
},
],
});
// Fallback will skip unhealthy providers
await ductape.fallback.create({
name: 'Smart Payment Fallback',
tag: 'smart-payment',
description: 'Provider fallback with health awareness',
input: {
amount: { type: DataTypes.NUMBER },
},
options: [
{
type: FeatureEventTypes.ACTION,
event: 'charge',
app: 'stripe',
input: { amount: '$Input{amount}' },
output: { id: '$Response{id}' },
retries: 1,
healthcheck: 'stripe-health', // Skip if unhealthy
},
{
type: FeatureEventTypes.ACTION,
event: 'charge',
app: 'paypal',
input: { amount: '$Input{amount}' },
output: { id: '$Response{id}' },
retries: 1,
healthcheck: 'paypal-health',
},
],
});
// Create healthchecks for providers
ductape.health.create(Map.of(
"name", "Stripe Health",
"tag", "stripe-health",
"app", "stripe",
"event", "ping",
"interval", 60000, // Check every minute
"retries", 2,
envs: [
Map.of(
"slug", "prd",
input: Map.of(),
),
],
));
// Fallback will skip unhealthy providers
ductape.fallback.create(Map.of(
"name", "Smart Payment Fallback",
"tag", "smart-payment",
"description", "Provider fallback with health awareness",
input: Map.of(
amount: Map.of( type: DataTypes.NUMBER ),
),
options: [
Map.of(
type: FeatureEventTypes.ACTION,
"event", "charge",
"app", "stripe",
input: Map.of( "amount", "$InputMap.of(amount)" ),
output: Map.of( "id", "$ResponseMap.of(id)" ),
"retries", 1,
"healthcheck", "stripe-health", // Skip if unhealthy
),
Map.of(
type: FeatureEventTypes.ACTION,
"event", "charge",
"app", "paypal",
input: Map.of( "amount", "$InputMap.of(amount)" ),
output: Map.of( "id", "$ResponseMap.of(id)" ),
"retries", 1,
"healthcheck", "paypal-health",
),
],
));
// Create healthchecks for providers
client.health.create({
"name": "Stripe Health",
"tag": "stripe-health",
"app": "stripe",
"event": "ping",
"interval": 60000, // Check every minute
"retries": 2,
envs: [
{
"slug": "prd",
input: {},
},
],
});
// Fallback will skip unhealthy providers
client.fallback.create({
"name": "Smart Payment Fallback",
"tag": "smart-payment",
"description": "Provider fallback with health awareness",
input: {
amount: { type: DataTypes.NUMBER },
},
options: [
{
type: FeatureEventTypes.ACTION,
"event": "charge",
"app": "stripe",
input: { "amount": "$Input{amount}" },
output: { "id": "$Response{id}" },
"retries": 1,
"healthcheck": "stripe-health", // Skip if unhealthy
},
{
type: FeatureEventTypes.ACTION,
"event": "charge",
"app": "paypal",
input: { "amount": "$Input{amount}" },
output: { "id": "$Response{id}" },
"retries": 1,
"healthcheck": "paypal-health",
},
],
});
// Create healthchecks for providers
await ductape.health.create({
["name"] = "Stripe Health",
["tag"] = "stripe-health",
["app"] = "stripe",
["event"] = "ping",
["interval"] = 60000, // Check every minute
["retries"] = 2,
envs: [
{
["slug"] = "prd",
input: {},
},
],
});
// Fallback will skip unhealthy providers
await ductape.fallback.create({
["name"] = "Smart Payment Fallback",
["tag"] = "smart-payment",
["description"] = "Provider fallback with health awareness",
input: {
amount: { type: DataTypes.NUMBER },
},
options: [
{
type: FeatureEventTypes.ACTION,
["event"] = "charge",
["app"] = "stripe",
input: { ["amount"] = "$Input{amount}" },
output: { ["id"] = "$Response{id}" },
["retries"] = 1,
["healthcheck"] = "stripe-health", // Skip if unhealthy
},
{
type: FeatureEventTypes.ACTION,
["event"] = "charge",
["app"] = "paypal",
input: { ["amount"] = "$Input{amount}" },
output: { ["id"] = "$Response{id}" },
["retries"] = 1,
["healthcheck"] = "paypal-health",
},
],
});
Best Practices
- Order providers by preference (cost, reliability, features)
- Set appropriate retries - balance between resilience and latency
- Use healthchecks to avoid trying known-down providers
- Standardize output mapping across providers for consistency
- Monitor fallback usage to detect provider issues
- Test fallback chains regularly to ensure they work when needed
- Document provider differences in input/output requirements
- Track costs - fallbacks may route to more expensive providers
- Set timeouts to prevent slow providers from blocking fallbacks
See Also
- Healthchecks - Monitor provider availability
- Features - Integrate fallbacks into workflows
- Actions - Create provider actions for fallback options