Testing Webhooks
Option 1 — Test via Client Portal
Go to Client Portal → Dashboard → Webhooks and click "Send Test Webhook".
TokenCashFlow sends a synthetic payment.completed-shaped payload to your configured URL and shows you:
- Whether delivery succeeded (HTTP status code from your server)
- The payload that was sent
The test delivery is signed with your configured secret. Use this to verify your signature verification code end-to-end.
Option 2 — Test via API
curl -X POST https://api.tokencashflow.com/v1/webhooks/test \
-H "X-API-Key: tcf_live_..."Response:
{
"success": true,
"data": {
"delivered": true,
"status_code": 200,
"response_body": "",
"payload_sent": {
"event": "payment.completed",
"timestamp": "2026-05-16T12:00:00Z",
"data": {
"payment_id": "test-00000000-0000-0000-0000-000000000000",
"status": "COMPLETED",
"amount_usd": 100.00,
"received_crypto": "0.05",
"crypto_symbol": "ETH",
"chain": "ethereum",
"locked_rate_usd": 2000.00,
"fees": { "service_fee_usd": 0.5, "conversion_fee_usd": 1.0 },
"net_amount_usd": 98.50
}
}
}
}Option 3 — Local development with a tunnel
During development your webhook handler runs on localhost, which is not reachable by TokenCashFlow's servers. Use a tunnelling tool to expose a local port publicly.
Using ngrok
# Install ngrok: https://ngrok.com/download
ngrok http 5000
# Output: https://a1b2c3d4.ngrok.io → http://localhost:5000
# Configure that URL in the Client Portal:
# https://a1b2c3d4.ngrok.io/webhookUsing Cloudflare Tunnel (free, no account needed for quick tests)
# macOS/Linux
brew install cloudflared # or download from https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/
cloudflared tunnel --url http://localhost:5000
# Output: https://random-name.trycloudflare.com
# Configure that URL in the Client PortalMinimal test server
Start a local server to log incoming webhooks during development:
# test_webhook_server.py
import hmac, hashlib, json
from flask import Flask, request
app = Flask(__name__)
SECRET = "your_webhook_secret_here"
@app.route("/webhook", methods=["POST"])
def webhook():
body = request.get_data()
sig = request.headers.get("X-TCF-Signature", "")
expected = "sha256=" + hmac.new(SECRET.encode(), body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, sig):
print("⚠️ Signature mismatch!")
return "Bad signature", 400
payload = json.loads(body)
print(f"✅ [{payload['event']}] payment_id={payload['data'].get('payment_id')}")
print(json.dumps(payload, indent=2))
return "", 200
if __name__ == "__main__":
app.run(port=5000)Run with:
pip install flask
python test_webhook_server.pyVerifying signature in test deliveries
Both the portal test button and the API POST /v1/webhooks/test sign the payload with your configured secret. Your signature verification code should handle test deliveries identically to real ones — there is no separate test mode for signatures.

