Skip to main content

Webhooks

STOA Platform can notify external systems when subscription lifecycle events occur. Webhooks deliver HTTP POST requests to your endpoints with signed payloads.

Event Types​

EventTriggerPayload Includes
subscription.createdNew subscription requestSubscription ID, API, consumer, plan
subscription.approvedSubscription approved by adminSubscription ID, API key (if generated)
subscription.revokedSubscription revokedSubscription ID, reason
subscription.key_rotatedAPI key rotatedSubscription ID, new key prefix
subscription.expiredSubscription TTL expiredSubscription ID, expiry date

Use ["*"] to subscribe to all event types.

Creating a Webhook​

Via API​

Configure your environment

The examples below use environment variables. Set them for your STOA instance:

export STOA_API_URL="https://api.gostoa.dev"       # Replace with your domain
export STOA_AUTH_URL="https://auth.gostoa.dev" # Keycloak OIDC provider
export STOA_GATEWAY_URL="https://mcp.gostoa.dev" # MCP Gateway endpoint

Self-hosted? Replace gostoa.dev with your domain.

curl -X POST "${STOA_API_URL}/v1/tenants/{tenant_id}/webhooks" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-system.example.com/stoa-events",
"events": ["subscription.created", "subscription.approved"],
"secret": "your-hmac-secret-min-32-chars-long",
"enabled": true,
"custom_headers": {
"X-Source": "stoa-platform"
}
}'

Via Portal​

  1. Navigate to Webhooks in the Portal sidebar
  2. Click Create Webhook
  3. Enter the target URL and select event types
  4. Set a signing secret (min 32 characters)
  5. Optionally add custom headers
  6. Click Save

Payload Format​

Every webhook delivery sends a JSON payload:

{
"event": "subscription.approved",
"timestamp": "2026-02-13T10:30:00Z",
"webhook_id": "wh_abc123",
"data": {
"subscription_id": "sub_xyz789",
"api_id": "api_456",
"api_name": "Payment API",
"consumer_id": "consumer_012",
"plan": "standard",
"status": "approved"
}
}

HMAC-SHA256 Signature Verification​

Every delivery includes a signature header for payload verification:

X-STOA-Signature: sha256=<hex-encoded-hmac>

Verification Example (Python)​

import hmac
import hashlib

def verify_signature(payload: bytes, secret: str, signature_header: str) -> bool:
expected = hmac.new(
secret.encode(),
payload,
hashlib.sha256
).hexdigest()
received = signature_header.removeprefix("sha256=")
return hmac.compare_digest(expected, received)

Verification Example (Node.js)​

const crypto = require('crypto');

function verifySignature(payload, secret, signatureHeader) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
const received = signatureHeader.replace('sha256=', '');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(received)
);
}

Always use timing-safe comparison to prevent timing attacks.

Retry Policy​

Failed deliveries (non-2xx response or timeout) are retried with exponential backoff:

AttemptDelayCumulative Wait
1Immediate0
21 minute1 min
35 minutes6 min
415 minutes21 min
51 hour1h 21min

After 5 failed attempts, the delivery is marked as failed. No further retries are attempted automatically.

Delivery Tracking​

View Delivery History​

curl "${STOA_API_URL}/v1/tenants/{tenant_id}/webhooks/{webhook_id}/deliveries" \
-H "Authorization: Bearer $TOKEN"

Each delivery record includes:

FieldDescription
statussuccess, failed, pending
status_codeHTTP response code from your endpoint
response_bodyFirst 1KB of response (for debugging)
attemptAttempt number (1-5)
created_atDelivery timestamp

Retry a Failed Delivery​

curl -X POST "${STOA_API_URL}/v1/tenants/{tenant_id}/webhooks/{webhook_id}/deliveries/{delivery_id}/retry" \
-H "Authorization: Bearer $TOKEN"

Testing Webhooks​

Send a test event to verify your endpoint before going live:

curl -X POST "${STOA_API_URL}/v1/tenants/{tenant_id}/webhooks/{webhook_id}/test" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"event_type": "subscription.created"}'

The test delivery uses synthetic data and is marked as test: true in the payload.

Managing Webhooks​

List Webhooks​

curl "${STOA_API_URL}/v1/tenants/{tenant_id}/webhooks" \
-H "Authorization: Bearer $TOKEN"

Disable a Webhook​

curl -X PATCH "${STOA_API_URL}/v1/tenants/{tenant_id}/webhooks/{webhook_id}" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"enabled": false}'

Delete a Webhook​

curl -X DELETE "${STOA_API_URL}/v1/tenants/{tenant_id}/webhooks/{webhook_id}" \
-H "Authorization: Bearer $TOKEN"

Best Practices​

  1. Always verify signatures β€” reject unsigned or incorrectly signed payloads
  2. Respond quickly β€” return 200 within 5 seconds; process asynchronously
  3. Handle duplicates β€” use webhook_id + timestamp for idempotency
  4. Use HTTPS β€” webhook URLs must use TLS in production
  5. Rotate secrets β€” update the webhook secret periodically via PATCH
  6. Monitor deliveries β€” check the Portal delivery history for failures