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)

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

openssl genrsa -out key.pem 2048

2

Generate a Certificate Signing Request

You'll be prompted for information like Country Name, State, Organization Name, and Common Name

openssl req -new -sha256 -key key.pem -out csr.csr

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

openssl req -x509 -sha256 -days 365 -key key.pem -in csr.csr -out certificate.pem

5

1.5 Copy the Base64 Encoded Certificate

Use this single-line value as your signingCertificate

# macOS
cat certificateBase64.pem | tr -d '\n' | pbcopy

# Linux
cat certificateBase64.pem | tr -d '\n' | xclip -selection clipboard

# Or manually copy the output
cat certificateBase64.pem | tr -d '\n'

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:

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 hmacSharedSecretKey is 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:

  1. Compute HMAC-SHA256 of the raw, unmodified request body using your shared secret key.
  2. Base64 encode the resulting hash.
  3. 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 changing payloadVerificationMethod:
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


© 2026 Tango API are provided by Tango, a division of BHN, Inc.