Product Webhooks
Manage webhooks for product apps and generate webhook URLs.
Overview
Product webhooks allow external services to send events to your product. When connected apps trigger events (like payment success, email bounce, etc.), they can notify your product via webhooks.
Fetching App Webhooks
Get all webhooks for a connected app:
- TypeScript
- Java
- Go
- .NET
import Ductape from '@ductape/sdk';
const ductape = new Ductape({
accessKey: 'your-access-key',
});
await ductape.product.init('my-product');
const webhooks = await ductape.product.webhook.fetchAllForProductApp('stripe-payments');
webhooks.forEach(webhook => {
console.log(`Webhook: ${webhook.event}`);
console.log(`URL: ${webhook.url}`);
});
import app.ductape.sdk.Ductape;
import app.ductape.sdk.core.EnvType;
import app.ductape.sdk.core.RequestContext;
RequestContext auth = new RequestContext(null, null, null, null, 'your-access-key');
Ductape ductape = new Ductape(EnvType.PRODUCTION, auth);
ductape.product.init('my-product');
Map<String, Object> webhooks = ductape.product.webhook.fetchAllForProductApp('stripe-payments');
webhooks.forEach(webhook => Map.of(
System.out.println(`Webhook: $Map.of(webhook.event)`);
System.out.println(`URL: $Map.of(webhook.url)`);
));
import (
"context"
"github.com/ductape/ductape/sdk/go/core"
ductapesdk "github.com/ductape/ductape/sdk/go/ductape"
)
auth := core.NewRequestContext("", "", "", "", 'your-access-key')
client, err := ductapesdk.New(core.EnvProduction, auth)
if err != nil {
return err
}
client.product.init('my-product');
webhooks := client.product.webhook.fetchAllForProductApp('stripe-payments');
webhooks.forEach(webhook => {
fmt.Println(`Webhook: ${webhook.event}`);
fmt.Println(`URL: ${webhook.url}`);
});
using Ductape.Sdk;
using Ductape.Sdk.Core;
var auth = new RequestContext(null, null, null, null, 'your-access-key', null);
var ductape = new Ductape(EnvType.Production, auth);
await ductape.product.init('my-product');
var webhooks = await ductape.product.webhook.fetchAllForProductApp('stripe-payments');
webhooks.forEach(webhook => {
Console.WriteLine(`Webhook: ${webhook.event}`);
Console.WriteLine(`URL: ${webhook.url}`);
});
Generating Webhook Links
Create webhook URLs for external services to call:
- TypeScript
- Java
- Go
- .NET
const webhookUrl = await ductape.product.webhook.generateLink({
app: 'stripe-payments',
event: 'payment.succeeded',
env: 'prd',
product: 'my-product',
});
console.log('Webhook URL:', webhookUrl);
// Use this URL in Stripe dashboard
Map<String, Object> webhookUrl = ductape.product.webhook.generateLink(Map.of(
"app", "stripe-payments",
"event", "payment.succeeded",
"env", "prd",
"product", "my-product"
));
System.out.println('Webhook URL:', webhookUrl);
// Use this URL in Stripe dashboard
webhookUrl := client.product.webhook.generateLink({
"app": "stripe-payments",
"event": "payment.succeeded",
"env": "prd",
"product": "my-product",
});
fmt.Println('Webhook URL:', webhookUrl);
// Use this URL in Stripe dashboard
var webhookUrl = await ductape.product.webhook.generateLink({
["app"] = "stripe-payments",
["event"] = "payment.succeeded",
["env"] = "prd",
["product"] = "my-product",
});
Console.WriteLine('Webhook URL:', webhookUrl);
// Use this URL in Stripe dashboard
Webhook Link Parameters
| Parameter | Type | Description |
|---|---|---|
app | string | App access tag |
event | string | Event name/tag |
env | string | Environment slug |
product | string | Product tag |
Complete Example
- TypeScript
- Java
- Go
- .NET
import Ductape from '@ductape/sdk';
async function setupWebhooks() {
const ductape = new Ductape({
accessKey: 'your-access-key',
});
await ductape.product.init('ecommerce-platform');
// Generate webhook URLs for Stripe events
const paymentSuccessUrl = await ductape.product.webhook.generateLink({
app: 'stripe-payments',
event: 'payment.succeeded',
env: 'prd',
product: 'ecommerce-platform',
});
const paymentFailedUrl = await ductape.product.webhook.generateLink({
app: 'stripe-payments',
event: 'payment.failed',
env: 'prd',
product: 'ecommerce-platform',
});
const refundUrl = await ductape.product.webhook.generateLink({
app: 'stripe-payments',
event: 'charge.refunded',
env: 'prd',
product: 'ecommerce-platform',
});
console.log('Configure these URLs in Stripe dashboard:');
console.log('Payment Success:', paymentSuccessUrl);
console.log('Payment Failed:', paymentFailedUrl);
console.log('Refund:', refundUrl);
// Fetch all configured webhooks
const webhooks = await ductape.product.webhook.fetchAllForProductApp('stripe-payments');
console.log(`Total webhooks configured: ${webhooks.length}`);
}
setupWebhooks().catch(console.error);
import app.ductape.sdk.Ductape;
import app.ductape.sdk.core.EnvType;
import app.ductape.sdk.core.RequestContext;
async function setupWebhooks() Map.of(
RequestContext auth = new RequestContext(null, null, null, null, 'your-access-key');
Ductape ductape = new Ductape(EnvType.PRODUCTION, auth);
ductape.product.init('ecommerce-platform');
// Generate webhook URLs for Stripe events
Map<String, Object> paymentSuccessUrl = ductape.product.webhook.generateLink(Map.of(
"app", "stripe-payments",
"event", "payment.succeeded",
"env", "prd",
"product", "ecommerce-platform"
));
Map<String, Object> paymentFailedUrl = ductape.product.webhook.generateLink(Map.of(
"app", "stripe-payments",
"event", "payment.failed",
"env", "prd",
"product", "ecommerce-platform"
));
Map<String, Object> refundUrl = ductape.product.webhook.generateLink(Map.of(
"app", "stripe-payments",
"event", "charge.refunded",
"env", "prd",
"product", "ecommerce-platform"
));
System.out.println('Configure these URLs in Stripe "dashboard", ");
System.out.println("Payment "Success", ", paymentSuccessUrl);
System.out.println("Payment "Failed", ", paymentFailedUrl);
System.out.println(""Refund", ", refundUrl);
// Fetch all configured webhooks
Map<String, Object> webhooks = ductape.product.webhook.fetchAllForProductApp("stripe-payments');
System.out.println(`Total webhooks configured: $Map.of(webhooks.length)`);
)
setupWebhooks();
import (
"context"
"github.com/ductape/ductape/sdk/go/core"
ductapesdk "github.com/ductape/ductape/sdk/go/ductape"
)
async function setupWebhooks() {
auth := core.NewRequestContext("", "", "", "", 'your-access-key')
client, err := ductapesdk.New(core.EnvProduction, auth)
if err != nil {
return err
}
client.product.init('ecommerce-platform');
// Generate webhook URLs for Stripe events
paymentSuccessUrl := client.product.webhook.generateLink({
"app": "stripe-payments",
"event": "payment.succeeded",
"env": "prd",
"product": "ecommerce-platform",
});
paymentFailedUrl := client.product.webhook.generateLink({
"app": "stripe-payments",
"event": "payment.failed",
"env": "prd",
"product": "ecommerce-platform",
});
refundUrl := client.product.webhook.generateLink({
"app": "stripe-payments",
"event": "charge.refunded",
"env": "prd",
"product": "ecommerce-platform",
});
fmt.Println('Configure these URLs in Stripe "dashboard": ");
fmt.Println("Payment "Success": ", paymentSuccessUrl);
fmt.Println("Payment "Failed": ", paymentFailedUrl);
fmt.Println(""Refund": ", refundUrl);
// Fetch all configured webhooks
webhooks := client.product.webhook.fetchAllForProductApp("stripe-payments');
fmt.Println(`Total webhooks configured: ${webhooks.length}`);
}
setupWebhooks().catch(console.error);
using Ductape.Sdk;
using Ductape.Sdk.Core;
async function setupWebhooks() {
var auth = new RequestContext(null, null, null, null, 'your-access-key', null);
var ductape = new Ductape(EnvType.Production, auth);
await ductape.product.init('ecommerce-platform');
// Generate webhook URLs for Stripe events
var paymentSuccessUrl = await ductape.product.webhook.generateLink({
["app"] = "stripe-payments",
["event"] = "payment.succeeded",
["env"] = "prd",
["product"] = "ecommerce-platform",
});
var paymentFailedUrl = await ductape.product.webhook.generateLink({
["app"] = "stripe-payments",
["event"] = "payment.failed",
["env"] = "prd",
["product"] = "ecommerce-platform",
});
var refundUrl = await ductape.product.webhook.generateLink({
["app"] = "stripe-payments",
["event"] = "charge.refunded",
["env"] = "prd",
["product"] = "ecommerce-platform",
});
Console.WriteLine('Configure these URLs in Stripe ["dashboard"] = ");
Console.WriteLine("Payment ["Success"] = ", paymentSuccessUrl);
Console.WriteLine("Payment ["Failed"] = ", paymentFailedUrl);
Console.WriteLine("["Refund"] = ", refundUrl);
// Fetch all configured webhooks
var webhooks = await ductape.product.webhook.fetchAllForProductApp("stripe-payments');
Console.WriteLine(`Total webhooks configured: ${webhooks.length}`);
}
setupWebhooks().catch(console.error);
Use Cases
Payment Webhooks
- TypeScript
- Java
- Go
- .NET
// Stripe payment events
const stripeWebhooks = [
'payment.succeeded',
'payment.failed',
'charge.refunded',
'customer.created',
];
for (const event of stripeWebhooks) {
const url = await ductape.product.webhook.generateLink({
app: 'stripe-payments',
event,
env: 'prd',
product: 'payment-service',
});
console.log(`${event}: ${url}`);
}
// Stripe payment events
Map<String, Object> stripeWebhooks = [
'payment.succeeded',
'payment.failed',
'charge.refunded',
'customer.created',
];
for (Map<String, Object> event of stripeWebhooks) Map.of(
Map<String, Object> url = ductape.product.webhook.generateLink(Map.of(
"app", "stripe-payments",
event,
"env", "prd",
"product", "payment-service"
));
System.out.println(`$Map.of(event): $Map.of(url)`);
)
// Stripe payment events
stripeWebhooks := [
'payment.succeeded',
'payment.failed',
'charge.refunded',
'customer.created',
];
for (const event of stripeWebhooks) {
url := client.product.webhook.generateLink({
"app": "stripe-payments",
event,
"env": "prd",
"product": "payment-service",
});
fmt.Println(`${event}: ${url}`);
}
// Stripe payment events
var stripeWebhooks = [
'payment.succeeded',
'payment.failed',
'charge.refunded',
'customer.created',
];
for (var event of stripeWebhooks) {
var url = await ductape.product.webhook.generateLink({
["app"] = "stripe-payments",
event,
["env"] = "prd",
["product"] = "payment-service",
});
Console.WriteLine(`${event}: ${url}`);
}
Email Webhooks
- TypeScript
- Java
- Go
- .NET
// SendGrid email events
const emailWebhooks = [
'email.delivered',
'email.opened',
'email.bounced',
'email.clicked',
];
for (const event of emailWebhooks) {
const url = await ductape.product.webhook.generateLink({
app: 'sendgrid-email',
event,
env: 'prd',
product: 'notification-service',
});
console.log(`${event}: ${url}`);
}
// SendGrid email events
Map<String, Object> emailWebhooks = [
'email.delivered',
'email.opened',
'email.bounced',
'email.clicked',
];
for (Map<String, Object> event of emailWebhooks) Map.of(
Map<String, Object> url = ductape.product.webhook.generateLink(Map.of(
"app", "sendgrid-email",
event,
"env", "prd",
"product", "notification-service"
));
System.out.println(`$Map.of(event): $Map.of(url)`);
)
// SendGrid email events
emailWebhooks := [
'email.delivered',
'email.opened',
'email.bounced',
'email.clicked',
];
for (const event of emailWebhooks) {
url := client.product.webhook.generateLink({
"app": "sendgrid-email",
event,
"env": "prd",
"product": "notification-service",
});
fmt.Println(`${event}: ${url}`);
}
// SendGrid email events
var emailWebhooks = [
'email.delivered',
'email.opened',
'email.bounced',
'email.clicked',
];
for (var event of emailWebhooks) {
var url = await ductape.product.webhook.generateLink({
["app"] = "sendgrid-email",
event,
["env"] = "prd",
["product"] = "notification-service",
});
Console.WriteLine(`${event}: ${url}`);
}
Environment-Specific Webhooks
Generate different webhook URLs for each environment:
- TypeScript
- Java
- Go
- .NET
const environments = ['dev', 'stg', 'prd'];
for (const env of environments) {
const url = await ductape.product.webhook.generateLink({
app: 'stripe-payments',
event: 'payment.succeeded',
env,
product: 'payment-service',
});
console.log(`${env}: ${url}`);
}
Map<String, Object> environments = ['dev', 'stg', 'prd'];
for (Map<String, Object> env of environments) Map.of(
Map<String, Object> url = ductape.product.webhook.generateLink(Map.of(
"app", "stripe-payments",
"event", "payment.succeeded",
env,
"product", "payment-service"
));
System.out.println(`$Map.of(env): $Map.of(url)`);
)
environments := ['dev', 'stg', 'prd'];
for (const env of environments) {
url := client.product.webhook.generateLink({
"app": "stripe-payments",
"event": "payment.succeeded",
env,
"product": "payment-service",
});
fmt.Println(`${env}: ${url}`);
}
var environments = ['dev', 'stg', 'prd'];
for (var env of environments) {
var url = await ductape.product.webhook.generateLink({
["app"] = "stripe-payments",
["event"] = "payment.succeeded",
env,
["product"] = "payment-service",
});
Console.WriteLine(`${env}: ${url}`);
}
Webhook Security
When receiving webhook calls:
- Verify signatures - Validate webhook signatures from providers
- Use HTTPS - Always use secure connections
- Validate payload - Check payload structure and content
- Idempotency - Handle duplicate webhook deliveries
- Respond quickly - Return 200 status promptly
Best Practices
- Generate once - Create webhook URLs during setup, not on every request
- Document events - Keep track of which webhooks are configured where
- Test webhooks - Use development environments to test webhook handling
- Monitor failures - Track failed webhook deliveries
- Version events - Include versions in event names if needed
See Also
- Products Overview - Product architecture
- Connecting Apps - Link apps to products
- App Webhooks - Configure webhooks at the app level
- Features - Process webhook events in features