Create signing certificate and HMAC secret
Follow the steps below to create two supported authentication methods used to secure Tango webhook integrations: signing Certificate and HMAC shared secret key. Webhook payload verification ensures that the event has been originated from our platform and the message body has not been modified in transit. Only authorized systems can successfully send webhook requests. Verification prevents spoofing, tampering, and replay attempts.
The sections below introduce each available verification method and no-auth mode, explaining how to choose and generate the appropriate methods to secure your webhook endpoint:
- Method 1: Webhook signature authentication (X509) (recommended)
- Method 2: Webhook signature authentication (HMAC)
- Method 3: No payload verification (NONE)
Method 1: Webhook signature authentication (X509)(recommended)
With X509, Tango signs each webhook payload with an X.509 certificate you provide. You then verify the signature using the corresponding public certificate.
Step 1. Generate Your X509 certificate
X.509 verification requires a public/private key pair. The table below outlines how to generate and format your certificate before adding it to your webhook subscription:
Step | Description | Example | |
|---|---|---|---|
1 | Generate a private key | This creates a 2048-bit RSA private key. Keep this file secure and never share it | |
2 | Generate a Certificate Signing Request | You'll be prompted for information like Country Name, State, Organization Name, and Common Name | |
3 | Create a self-signed X509 certificate | This creates a certificate valid for 365 days. After expiration, generate a new certificate and update your subscription | |
5 | 1.5 Copy the Base64 Encoded Certificate | Use this single-line value as your | |
Step 2: Create a subscription with X509
Now that you have your X.509 certificate, you can create a webhook subscription that uses it for payload signing. Tango will use this certificate to sign all webhook payloads it sends to your endpoint:
curl -X POST https://api.example.com/subscriptions \
-H "Content-Type: application/json" \
-H "Authorization: Basic YOUR_BASIC_AUTH_TOKEN" \
-d '{
"url": "https://your-webhook-endpoint.com/webhook",
"headers": [],
"categories": [],
"eventTypes": ["AsyncOrderStatus"],
"payloadVerificationMethod": "X509",
"signingCertificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURD..."
}'
Step 3: Verify incoming webhooks (client side)
When your server receives a webhook from Tango, it includes an X-Tango-Signature header containing the digital signature. Use the public key from the X.509 certificate you configured to verify this signature against the raw, unmodified HTTP request body using an RSA‑SHA256 signature verification routine. Only if the signature is valid should you trust and process the webhook payload.
Note:X.509 certificates are valid for a limited period (typically 365 days). When your certificate expires—or before it expires to avoid downtime—you must rotate it by providing a new certificate to Tango. To rotate your X.509 certificate:
- Generate a new certificate (Steps 1–5).
- Update your subscription using PATCH. See Switch verification methods on a subscription.
- Do not resend
payloadVerificationMethodunless switching methods.
curl --request PATCH \
--url https://integration-api.tangocard.com/raas/v2/webhooks/{webhookId}\
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"signingCertificate": "NEW_BASE64_SINGLE_LINE_CERT"
}'
Method 2: Webhook signature authentication (HMAC)
HMAC-SHA256 is a simpler alternative to X. 509 to secure and authenticate Tango webhook requests. With HMAC, there is no public/private key pair. Instead, both you and Tango share a single secret key. Tango uses this secret to sign each webhook payload, and your server uses the same secret to verify that the payload was not tampered with and truly came from Tango.
Step 1: Generate an HMAC shared secret key
To use HMAC, the first step is generating a secure secret key. This key is what Tango will use to sign webhook payloads using HMAC‑SHA256, and what your server will use to validate those signatures.
# Generate a 32-byte random key and Base64 encode it
openssl rand -base64 32
# Example output: dGhpc2lzYXNlY3VyZTMyYnl0ZXNlY3JldGtleWZvcmhtYWNzaGEyNTY=
Step 2: Create a subscription with HMAC
This step shows how to create a new webhook subscription where HMAC-SHA256 is used to authenticate every webhook payload sent to your endpoint. Instead of using certificates (X.509), you provide Tango with a shared secret key, and Tango uses that secret to cryptographically sign each webhook request.
curl -X POST https://api.example.com/subscriptions \
-H "Content-Type: application/json" \
-H "Authorization: Basic YOUR_BASIC_AUTH_TOKEN" \
-d '{
"url": "https://your-webhook-endpoint.com/webhook",
"headers": [],
"categories": [],
"eventTypes": ["AsyncOrderStatus"],
"payloadVerificationMethod": "HMAC",
"hmacSharedSecretKey": "dGhpc2lzYXNlY3VyZTMyYnl0ZXNlY3JldGtleWZvcmhtYWNzaGEyNTY="
}'
NOTE:
- Store your HMAC shared secret key securely at the time of creation - it cannot be retrieved in full after that. If you lose the key, you will need to generate a new one and update your subscription.
- HMAC Key is masked in API Responses. Tango never returns your full HMAC shared secret key in any API response—even when you fetch or update a webhook subscription. Instead, Tango masks the key so only the first two and last two characters are visible. For security purposes, the
hmacSharedSecretKeyis never returned in full in GET or PATCH responses. You will always receive a masked version showing only the first two and last two characters:{ "webhookId": "d4df9552-13b8-4257-ae1d-9785c6ef1eb4", "url": "http://localhost:3000/webhook", "headers": [ { "name": "Content-Type", "value": "application/json" } ], "categories": [], "eventTypes": ["AsyncOrderStatus"], "signingCertificate": null, "hmacSharedSecretKey": "dG***********Y=", "createdAt": "2026-02-22T23:28:24.55166-06:00", "updatedAt": "2026-02-22T23:28:24.55166-06:00", "expiresAt": "2027-02-22T23:28:24.55051-06:00", "payloadVerificationMethod": "HMAC" }
Step 3: Verify webhook (client side)
When your server receives a webhook, Tango includes an X-Tango-Signature header containing the HMAC‑SHA256 signature of the payload. To validate the request:
- Compute HMAC-SHA256 of the raw, unmodified request body using your shared secret key.
- Base64 encode the resulting hash.
- Compare your computed value to the X-Tango-Signature header using a constant‑time comparison to prevent timing attacks.
Only if the values match should you trust and process the webhook payload. For key rotation (staying on HMAC), you can PATCH with a new key without changingpayloadVerificationMethod:
curl --request PATCH \
--url https://integration-api.tangocard.com/raas/v2/webhooks/{webhookId}\
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"hmacSharedSecretKey": "NEW_BASE64_ENCODED_SHARED_SECRET_KEY"
}'
Method 3: No payload verification (NONE)
If you do not require payload signing, set payloadVerificationMethod to NONE. No signingCertificate or hmacSharedSecretKey should be provided. No signature header will be included in webhook deliveries for these subscriptions.
curl -X POST https://api.example.com/subscriptions \
-H "Content-Type: application/json" \
-H "Authorization: Basic YOUR_BASIC_AUTH_TOKEN" \
-d '{
"url": "https://your-webhook-endpoint.com/webhook",
"headers": [],
"categories": [],
"eventTypes": ["AsyncOrderStatus"],
"payloadVerificationMethod": "NONE"
}'
Endpoint Authorization (Basic Auth or OAuth)
In addition to payload signing, you can protect your webhook endpoint using an authorization header included in webhook deliveries. Endpoint authorization is optional and independent of the payload verification method—you can use it with X509, HMAC, or NONE. Follow these steps for Basic Authentication::
Step 1: Create and encode your credentials
If you choose Basic Authentication, you start by generating credentials in the standard username/password format:
echo -n "webhook_user:secure_password_123" | base64
# Output: d2ViaG9va191c2VyOnNlY3VyZV9wYXNzd29yZF8xMjM=
Step 2: Include the credentials in the headers array when creating your subscription
You include these credentials as a custom header in your subscription request:
curl -X POST https://api.example.com/subscriptions \
-H "Content-Type: application/json" \
-H "Authorization: Basic YOUR_API_AUTH_TOKEN" \
-d '{
"url": "https://your-webhook-endpoint.com/webhook",
"headers": [
{
"name": "Authorization",
"value": "Basic d2ViaG9va191c2VyOnNlY3VyZV9wYXNzd29yZF8xMjM="
}
],
"eventTypes": ["AsyncOrderStatus"],
"payloadVerificationMethod": "X509",
"signingCertificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t..."
}'
Step 3: Common custom header patterns
This section shows different ways or patterns (reusable, real-world examples) to pass custom headers when creating a webhook subscription. These headers are used by your server to authenticate incoming webhook requests, making your webhook safer and easier to validate.
Each pattern shows a different way to include authentication or identification information in the "headers" array. Tango will attach these headers to every outbound webhook request, allowing your server to verify that the request truly originated from Tango and to enforce any additional security you require.
Pattern 1: Simple secret token
This pattern demonstrates the most basic way to authenticate incoming webhook requests using a static shared secret. When you create the webhook subscription, you include a custom header—X-Webhook-Secret—with a secret value that only you and Tango know.
curl --request POST \
--url https://integration-api.tangocard.com/raas/v2/webhooks \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"url": "https://your-domain.com/webhooks/tango",
"eventTypes": ["ItemAvailability"],
"headers": [
{
"name": "X-Webhook-Secret",
"value": "your-super-secret-key-12345"
}
]
}'
Pattern 2: API key header
This pattern shows how to authenticate webhook requests using an API key passed in a custom HTTP header. In this approach, you provide Tango with an API key when creating your webhook subscription, and Tango will include that same header in every webhook request it sends to your endpoint.
curl --request POST \
--url https://integration-api.tangocard.com/raas/v2/webhooks \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"url": "https://your-domain.com/webhooks/tango",
"eventTypes": ["ItemAvailability", "RewardStatus"],
"headers": [
{
"name": "X-API-Key",
"value": "api_key_abc123xyz"
}
]
}'
Pattern 3: Bearer token
This pattern shows how to authenticate webhook requests using a Bearer token placed in the Authorization header. With this approach, you provide Tango with a token that represents your internal authentication scheme. Tango will then include that same Authorization: Bearer header in every webhook it delivers to your endpoint.
curl --request POST \
--url https://integration-api.tangocard.com/raas/v2/webhooks \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"url": "https://your-domain.com/webhooks/tango",
"eventTypes": ["AccountStatus"],
"headers": [
{
"name": "Authorization",
"value": "Bearer my_internal_webhook_token_xyz"
}
]
}'
Pattern 4: Multiple headers (defense in depth)
This pattern demonstrates how to apply layered security to your webhook integration by sending multiple custom headers with every webhook request. Instead of relying on a single shared secret or token, you provide Tango with several independent headers. Tango includes all of them in each webhook delivery, and your server validates each one before accepting the request. This approach is known as defense in depth—using multiple authentication checks so that if one token is leaked, misconfigured, or bypassed, the others still protect your endpoint.
curl --request POST \
--url https://integration-api.tangocard.com/raas/v2/webhooks \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"url": "https://your-domain.com/webhooks/tango",
"eventTypes": ["ItemAvailability", "RewardStatus"],
"headers": [
{
"name": "X-Webhook-Secret",
"value": "secret-key-123"
},
{
"name": "X-API-Key",
"value": "api-key-456"
},
{
"name": "X-Source",
"value": "tango-webhooks"
}
]
}'
Manage OAuth 2.0 Bearer tokens in custom headers
If you use an OAuth 2.0 access token in your custom headers for Tango to authenticate to your endpoint, you must keep the token valid. OAuth access tokens expire after a set period (for example, 1 hour, 24 hours, or 30 days, depending on your provider). When a token expires, Tango’s webhook calls to your endpoint receive 401 Unauthorized responses from your server. To avoid authentication failure, you must:
- Monitor token expiration: Track when your tokens will expire.
- Refresh tokens proactively: Obtain new tokens before they expire.
- Update the webhook subscription: Use
PATCH {URI}/webhooks/{webhookId}to update the headers with the new token. - Avoid service disruptions: Ensure tokens are rotated before they expire.
For example, if you webhook endpoint uses OAuth 2.0, your tokens expire every 24 hours. To avoid authentication failures, you must proactively rotate your OAuth token—refresh it before expiration and update your webhook subscription with the new token so Tango’s webhook calls continue to succeed. Follow the steps below:
Step 1: Subscribe with the initial OAuth token
Create your webhook subscription and provide Tango with the initial OAuth 2.0 access token that your server requires for authenticating inbound webhook requests.
# Get your OAuth token from your auth provider
YOUR_OAUTH_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
# Create webhook subscription with OAuth token
curl --request POST \
--url https://integration-api.tangocard.com/raas/v2/webhooks \
--header 'Authorization: Bearer YOUR_TANGO_ACCESS_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"url": "https://your-domain.com/webhooks/tango",
"eventTypes": ["ItemAvailability", "AccountStatus", "RewardStatus"],
"payloadVerificationMethod": "X509",
"signingCertificate": "BASE64_SINGLE_LINE_CERT_HERE",
"headers": [
{
"name": "Authorization",
"value": "Bearer '"$YOUR_OAUTH_TOKEN"'"
}
]
}'
# Response includes webhookId - SAVE THIS!
# {
# "webhookId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
# "expiresAt": "2026-02-13T19:01:16.536Z"
# }
Step 2: Update the subscription when the token expires
Here shows the process you must follow after your OAuth 2.0 access token expires. Because OAuth tokens have a limited lifetime, the token you originally gave Tango (in the above example) will become invalid. Once it expires, your server will reject incoming webhook requests — typically returning 401 Unauthorized.
# Get new OAuth token from your auth provider
NEW_OAUTH_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...NEW_TOKEN..."
WEBHOOK_ID="3fa85f64-5717-4562-b3fc-2c963f66afa6"
curl --request PATCH \
--url https://integration-api.tangocard.com/raas/v2/webhooks/$WEBHOOK_ID \
--header 'Authorization: Bearer YOUR_TANGO_ACCESS_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"headers": [
{
"name": "Authorization",
"value": "Bearer '"$NEW_OAUTH_TOKEN"'"
}
]
}'
Best practices:
- Set up automated token refresh before expiration
- Monitor token expiration and renewal processes
- Consider using refresh tokens for seamless rotation
Updated about 5 hours ago
