Token Rotation

Overview

Token rotation is essential for maintaining security in your Direct Data Sharing integration. This guide provides information on managing your Direct Data Sharing tokens, including retrieving token metadata, rotating tokens, and updating token expiration times.

Important

The client ID and the client secret of the zdirect application are required for the Direct Data Sharing integration.

Note

The auth_token_url and dds_url in the example snippets below use the sandbox endpoints. When accessing your actual Zalando data, replace the sandbox URLs with:

  • Authentication URL: https://api.merchants.zalando.com/auth/token
  • Direct Data Sharing Token URL: https://api.merchants.zalando.com/dds-tokens

Token Operations

1. Retrieve Token Metadata - GET Method

Purpose: Fetch the metadata of existing tokens.

Usage: - List all tokens - View details such as activation state, creation time, and expiry time - Monitor token status (if the token is retrieved, the activation_link will be None)

Example Request:

import time
import os
import requests

auth_token_url = 'https://api-sandbox.merchants.zalando.com/auth/token'
client_id = "<to-be-replaced>"
client_secret = "<to-be-replaced>"

# Get the zdirect authentication token
auth_response = requests.post(
    auth_token_url,
    headers={'Content-Type': 'application/x-www-form-urlencoded'},
    data={
        "grant_type": "client_credentials",
        "client_id": client_id,
        "client_secret": client_secret,
        "scope": "access_token_only"
    }
)

access_token = auth_response.json().get("access_token")
print("Access token retrieved successfully.")

# Use the zdirect authentication token to call the Direct Data Sharing endpoint
dds_url = 'https://api-sandbox.merchants.zalando.com/dds-tokens'
headers = {
    "Authorization": f"Bearer {access_token}",
    "Content-Type": "application/json"
}

# use the method - GET
dds_response = requests.get(dds_url, headers=headers)

# print result or raise error
response = dds_response.json()
print(f"Direct Data Sharing Token Response: {response}")

Example Response:

{
  "tokens": [
    {
      "activation_link": null,
      "state": "ACTIVE",
      "created_at": "2026-03-31 T12:47:15.199000",
      "updated_at": "2026-03-31 T12:47:15.200000",
      "expiration_time_at": "2026-06-29 T12:47:15.200000"
    }
  ]
}

2. Rotate Tokens - POST Method

Purpose: Request the generation of a new token.

Usage: - Request a new token when no tokens exist - Request a new token when the existing token is about to expire - Regularly rotate tokens as part of your security policy

Important

  • At any given time, only 2 tokens can exist at maximum
  • Any token which is created has a default expiry time of 90 days
  • It is recommended that you set up automated token rotation to maintain and adhere to security in the Direct Data Sharing integration

Example Request:

# Get the access token (same as in GET method)
# ...

# Use the token to call the Direct Data Sharing endpoint
dds_url = 'https://api-sandbox.merchants.zalando.com/dds-tokens'
headers = {
    "Authorization": f"Bearer {access_token}",
    "Content-Type": "application/json"
}

# set the existing token expiry time
payload = {
    "existing_token_expiry_time_in_seconds": 1000, #change as required
    "reason": "Planned rotation"
}

# use the method - POST
dds_response = requests.post(dds_url, headers=headers, json=payload)

# Print result or raise error
dds_response.raise_for_status()
response = dds_response.json()
print(f"Direct Data Sharing Token Response: {response}")

Example Response:

{
  "tokens": [
    {
      "activation_link": null,
      "state": "ROTATED",
      "created_at": "2026-03-31 T15:36:17.226000",
      "updated_at": "2026-04-01 T08:05:56.467000",
      "expiration_time_at": "2026-04-01 T08:22:36.467000"
    },
    {
      "activation_link": "https://frankfurt.cloud.databricks.com/delta_sharing/retrieve_config.html?xxxxxxxxxxxxxxxxxxxxxxxxxxx-BsaZwUkO5jexBA8",
      "state": "ACTIVE",
      "created_at": "2026-04-01 T08:05:56.469000",
      "updated_at": "2026-04-01 T08:05:56.471000",
      "expiration_time_at": "2026-07-30 T08:05:56.471000"
    }
  ]
}

3. Update Token Expiration - PATCH Method

Purpose: Update the expiry time of existing tokens.

Usage: - Manually expire an existing token - Immediately revoke a compromised token (use value "0" for immediate expiration)

Example Request:

# retrieving the access token (same as in the GET method)
# ...

# Use the token to call the Direct Data Sharing endpoint
dds_url = 'https://api-sandbox.merchants.zalando.com/dds-tokens'
headers = {
    "Authorization": f"Bearer {access_token}",
    "Content-Type": "application/json"
}

# set the existing token expiry time
payload = {
    "existing_token_expiry_time_in_seconds": 100,
    "reason": "Planned expiration"
}

# use the method - PATCH
dds_response = requests.patch(dds_url, headers=headers, json=payload)

# Print result or raise error
dds_response.raise_for_status()
response = dds_response.json()
print(f"DDS Token Response: {response}")

Example Response:

{
  "tokens": [
    {
      "activation_link": null,
      "state": "ROTATED",
      "created_at": "2026-04-01 T08:09:15.618000",
      "updated_at": "2026-04-01 T08:09:35.267000",
      "expiration_time_at": "2026-04-01 T08:11:14.267000"
    },
    {
      "activation_link": null,
      "state": "ACTIVE",
      "created_at": "2026-04-01 T08:09:21.398000",
      "updated_at": "2026-04-01 T08:09:35.268000",
      "expiration_time_at": "2026-04-01 T08:11:14.268000"
    }
  ]
}

Token Lifecycle and States

  • Maximum tokens: At any given time, only 2 tokens can exist for an account
  • Token states:
  • ACTIVE: Token is ready to use
  • ROTATED: New token has been generated, awaiting activation or the old token is being phased out

Automated Token Rotation

The following code snippet demonstrates how to automatically rotate tokens 14 days before their expiry time.

Note

For the below script to work, there should be a token which is already active and in use.

import time
import requests
from datetime import datetime, timezone, timedelta

# Configuration
auth_token_url = 'https://api-sandbox.merchants.zalando.com/auth/token'
dds_url = 'https://api-sandbox.merchants.zalando.com/dds-tokens'
client_id = "<add-client-id>"
client_secret = "<add-client-secret>"

# Step 1: Get authentication token
auth_response = requests.post(
    auth_token_url,
    headers={'Content-Type': 'application/x-www-form-urlencoded'},
    data={
        "grant_type": "client_credentials",
        "client_id": client_id,
        "client_secret": client_secret,
        "scope": "access_token_only"
    }
)
auth_response.raise_for_status()
access_token = auth_response.json().get("access_token")
print("Access token retrieved successfully.")

# Step 2: Set headers
headers = {
    "Authorization": f"Bearer {access_token}",
    "Content-Type": "application/json"
}

# Step 3: Get Direct Data Sharing tokens
dds_response = requests.get(dds_url, headers=headers)
dds_response.raise_for_status()

response = dds_response.json()
tokens = response.get("tokens", [])
print("Direct Data Sharing Token Response:")
print(response)

# Step 4: Check if all tokens expire in <= 14 days
now = datetime.now(timezone.utc)

tokens_expiring_soon = all(
    datetime.fromisoformat(token["expiration_time_at"]).replace(tzinfo=timezone.utc) - now <= timedelta(days=14)
    for token in tokens
)

# Step 5: Rotate tokens
if tokens_expiring_soon:
    print("All tokens are expiring within the next 14 days. Rotating...")
    time.sleep(5)
    rotate_payload = {
        "existing_token_expiry_time_in_seconds": 1209400,
        "reason": "Planned rotation"
    }
    rotate_response = requests.post(dds_url, headers=headers, json=rotate_payload)
    rotate_response.raise_for_status()

    print("Token rotated successfully.")
    print(rotate_response.json())
else:
    print("At least one token is valid for more than 14 days. No rotation needed.")

Additional Resources

Contact Support