Skip to main content

Partner Authentication Guide - TickAndLive Connect TempsRéel API

🎯 Purpose: This guide explains how to authenticate and integrate the TickAndLive Connect TempsRéel API into your application using OAuth 2.0.

📚 For complete endpoint documentation: See the interactive API reference in the Developer Portal or Swagger UI at https://tempsreel.connect.tickandlive.com/swagger

💡 Diagram visualization: This document contains interactive Mermaid diagrams. To view them:

  • GitHub/GitLab: Diagrams display automatically
  • VS Code: Install the Markdown Preview Mermaid Support extension
  • Alternative: View this document directly on GitHub/GitLab

📋 Table of Contents

  1. Introduction
  2. Prerequisites
  3. OAuth 2.0 Authentication
  4. Making API Calls
  5. Required Headers
  6. Code Examples
  7. Error Handling
  8. Support

Introduction

The TickAndLive Connect TempsRéel API allows you to interact with ticketing systems for events managed through the Aparté by Tick&Live solution. This REST API replaces existing SOAP services and provides a modern, high-performance interface.

This guide focuses on authentication and technical integration. For detailed endpoint documentation, see the interactive API reference.

API Architecture:

  • REST API with JSON
  • OAuth 2.0 authentication (Azure AD / Entra ID)
  • Multi-tenant (requires x-tenant header)
  • Versioning in URL (/api/v1/...)

Prerequisites

You must receive the following information from TickAndLive:

{
"clientId": "12345678-90ab-cdef-1234-567890abcdef",
"clientSecret": "YourConfidentialSecret123!",
"tenantId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
"authority": "https://login.microsoftonline.com/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
"scope": "api://a1b2c3d4-e5f6-7890-abcd-ef1234567890/.default",
"apiBaseUrl": "https://tempsreel.connect.tickandlive.com",
"tenants": ["ffr", "stadefrance", "parcdesprinces"]
}

⚠️ Important notes:

  • The values above are fictitious examples. Use the credentials provided by TickAndLive.
  • The scope uses the format api://{guid}/.default where the GUID corresponds to the TempsRéel API App Registration (provided by TickAndLive).

⚠️ Security:

  • The clientSecret must be stored securely (Azure Key Vault, environment variables, etc.)
  • Never commit the secret to source control
  • The tenants list contains the customers you have access to

Obtaining Access

Contact support@tickandlive.com with:

  • Your organization name
  • Intended use case
  • Target environment (staging/production)

You will receive:

  • OAuth 2.0 Credentials (clientId, clientSecret, tenantId, scope)
  • Your tenant ID(s) (to use in the x-tenant header)
  • The API base URL
  • Access to the Developer Portal

OAuth 2.0 Authentication

The API uses OAuth 2.0 Client Credentials Flow with Azure AD (Entra ID). This method is designed for service-to-service authentication (without user interaction).

Why OAuth 2.0 Client Credentials?

  • Secure: No plaintext passwords, time-limited tokens
  • Standard: Widely supported OAuth 2.0 protocol
  • Centralized: Credential management via Azure AD
  • Revocable: Ability to revoke access instantly

Authentication Flow

sequenceDiagram
participant App as Your Application
participant AD as Azure AD
participant API as TempsRéel API

App->>AD: POST /token<br/>(clientId, clientSecret, scope)
AD->>App: Access Token JWT<br/>(valid 1h)

loop While token valid
App->>API: GET /api/v1/...<br/>Authorization: Bearer {token}<br/>x-tenant: ffr
API->>App: JSON Response
end

Note over App: Token expired (1h)
App->>AD: POST /token<br/>(refresh)
AD->>App: New token

Obtaining an Access Token

Azure AD Endpoint:

POST https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token

Required Headers:

Content-Type: application/x-www-form-urlencoded

Parameters (form-urlencoded):

ParameterValueDescription
grant_typeclient_credentialsAuthentication type (always this value)
client_idProvided by TickAndLiveYour Azure AD application identifier
client_secretProvided by TickAndLiveYour secret (keep secure)
scopeProvided by TickAndLiveAPI access scope
Format: api://{guid}/.default
Example: api://a1b2c3d4-e5f6-7890-abcd-ef1234567890/.default

Complete Request Example:

POST https://login.microsoftonline.com/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&client_id=12345678-90ab-cdef-1234-567890abcdef
&client_secret=YourConfidentialSecret123!
&scope=api://a1b2c3d4-e5f6-7890-abcd-ef1234567890/.default

Success Response (200 OK):

{
"token_type": "Bearer",
"expires_in": 3599,
"ext_expires_in": 3599,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik1yNS1BVWl..."
}

Response Fields:

FieldDescription
token_typeAlways "Bearer"
expires_inToken validity duration in seconds (typically 3599 = ~1h)
access_tokenThe JWT token to use for API calls

Token Lifetime and Renewal

⏱️ Validity Duration: The token is valid for 1 hour (3600 seconds)

Renewal Strategies:

  1. Reactive Approach: Wait for 401 error and request a new token

    • ❌ Generates additional latency
    • ❌ Requires a new request
  2. Proactive Approach ✅ (recommended):

    • Cache the token with its expiration time
    • Automatically renew 5 minutes before expiration
    • Avoids service interruptions

Making API Calls

Once you have the token, use it to call API endpoints.

Base URL

Staging:    https://tempsreel-pre.connect.tickandlive.com
Production: https://tempsreel.connect.tickandlive.com

General Endpoint Format

{baseUrl}/api/v{version}/{resource}/{action}

Examples:

  • GET /api/v1/catalog/events
  • POST /api/v1/cart/allocateBestSeats
  • POST /api/v1/order/confirm

Required Headers

Each API request must include these headers:

1. Authorization (required)

Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGc...

The JWT token obtained from Azure AD.

2. x-tenant (required)

x-tenant: ffr

Identifier of the tenant (customer) you want to access.

Possible Values: Provided by TickAndLive (e.g., ffr, stadefrance)

⚠️ Important: You can only access tenants authorized in your configuration.

3. Content-Type (for POST/PUT)

Content-Type: application/json

Complete Request Example

GET /api/v1/catalog/events?startDate=2026-01-01&endDate=2026-12-31 HTTP/1.1
Host: tempsreel-pre.connect.tickandlive.com
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGc...
x-tenant: ffr
Accept: application/json

Code Examples

This section provides implementation examples for integrating OAuth 2.0 authentication and calling the API.

C# (.NET 6+)

Uses the Microsoft.Identity.Client (MSAL) library to manage Azure AD authentication.

Install NuGet Package:

dotnet add package Microsoft.Identity.Client

Complete Code:

using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Threading.Tasks;
using Microsoft.Identity.Client;

public class TempsReelApiClient : IDisposable
{
private readonly HttpClient _httpClient;
private readonly IConfidentialClientApplication _authApp;
private readonly string[] _scopes;
private readonly string _apiBaseUrl;

public TempsReelApiClient(string clientId, string clientSecret,
string tenantId, string scope, string apiBaseUrl)
{
_httpClient = new HttpClient();
_apiBaseUrl = apiBaseUrl;
_scopes = new[] { scope };

// Configure MSAL for Client Credentials Flow
_authApp = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithClientSecret(clientSecret)
.WithAuthority(new Uri($"https://login.microsoftonline.com/{tenantId}"))
.Build();
}

/// <summary>
/// Gets an access token (MSAL automatically manages caching)
/// </summary>
private async Task<string> GetAccessTokenAsync()
{
var result = await _authApp
.AcquireTokenForClient(_scopes)
.ExecuteAsync();

return result.AccessToken;
}

/// <summary>
/// Performs a GET request to the API
/// </summary>
public async Task<T?> GetAsync<T>(string endpoint, string tenant)
{
var token = await GetAccessTokenAsync();

var request = new HttpRequestMessage(HttpMethod.Get, $"{_apiBaseUrl}{endpoint}");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
request.Headers.Add("x-tenant", tenant);

var response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();

return await response.Content.ReadFromJsonAsync<T>();
}

/// <summary>
/// Performs a POST request to the API
/// </summary>
public async Task<TResponse?> PostAsync<TRequest, TResponse>(
string endpoint, string tenant, TRequest body)
{
var token = await GetAccessTokenAsync();

var request = new HttpRequestMessage(HttpMethod.Post, $"{_apiBaseUrl}{endpoint}");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
request.Headers.Add("x-tenant", tenant);
request.Content = JsonContent.Create(body);

var response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();

return await response.Content.ReadFromJsonAsync<TResponse>();
}

public void Dispose()
{
_httpClient?.Dispose();
}
}

Usage Example:

using System;
using System.Threading.Tasks;

class Program
{
static async Task Main(string[] args)
{
// ⚠️ Never hardcode the secret - use environment variables
var clientSecret = Environment.GetEnvironmentVariable("TEMPSREEL_CLIENT_SECRET")
?? throw new InvalidOperationException("TEMPSREEL_CLIENT_SECRET not set");

using var client = new TempsReelApiClient(
clientId: "12345678-90ab-cdef-1234-567890abcdef",
clientSecret: clientSecret,
tenantId: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
scope: "api://a1b2c3d4-e5f6-7890-abcd-ef1234567890/.default",
apiBaseUrl: "https://tempsreel.connect.tickandlive.com"
);

try
{
// Example GET: Retrieve events
var events = await client.GetAsync<dynamic>(
"/api/v1/catalog/events?startDate=2026-01-01&endDate=2026-12-31",
tenant: "ffr"
);

Console.WriteLine($"Events retrieved: {events}");

// Example POST: Allocate seats
var cartResponse = await client.PostAsync<dynamic, dynamic>(
"/api/v1/cart/allocateBestSeats",
tenant: "ffr",
body: new
{
sessionId = 67890,
catalogId = 999,
quantity = 4,
priceCategory = "CAT1"
}
);

Console.WriteLine($"Cart created: {cartResponse}");
}
catch (HttpRequestException ex)
{
Console.WriteLine($"API Error: {ex.Message}");
}
}
}

Key Implementation Points:

MSAL automatically manages:

  • Token caching
  • Renewal before expiration
  • Retries on temporary errors

Security:

  • clientSecret is loaded from an environment variable
  • HttpClient is properly disposed

Reusable:

  • Generic methods for GET/POST/PUT
  • Automatic addition of required headers (Authorization, x-tenant)

Bash / cURL

For quick testing or integration into shell scripts.

Prerequisites: curl and jq (for JSON parsing)

#!/bin/bash

# Configuration - ⚠️ USE ENVIRONMENT VARIABLES IN PRODUCTION
CLIENT_ID="12345678-90ab-cdef-1234-567890abcdef"
CLIENT_SECRET="${TEMPSREEL_CLIENT_SECRET}" # From environment variable
TENANT_ID="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
SCOPE="api://fedcba98-7654-3210-fedc-ba9876543210/.default"
API_BASE_URL="https://tempsreel.connect.tickandlive.com"
TENANT="ffr"

# Check that secret is defined
if [ -z "$CLIENT_SECRET" ]; then
echo "Error: TEMPSREEL_CLIENT_SECRET is not defined"
exit 1
fi

echo "🔐 Requesting access token from Azure AD..."

# 1. Get OAuth 2.0 token
TOKEN_RESPONSE=$(curl -s -X POST \
"https://login.microsoftonline.com/$TENANT_ID/oauth2/v2.0/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=$CLIENT_ID" \
-d "client_secret=$CLIENT_SECRET" \
-d "scope=$SCOPE")

# Extract token
ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.access_token')

# Verify token was obtained
if [ "$ACCESS_TOKEN" = "null" ] || [ -z "$ACCESS_TOKEN" ]; then
echo "❌ Error obtaining token:"
echo "$TOKEN_RESPONSE" | jq .
exit 1
fi

echo "✅ Token obtained: ${ACCESS_TOKEN:0:50}..."

# 2. Call API - GET Example
echo ""
echo "📡 Calling API GET /api/v1/catalog/events..."

curl -X GET \
"$API_BASE_URL/api/v1/catalog/events?startDate=2026-01-01&endDate=2026-12-31" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "x-tenant: $TENANT" \
-H "Accept: application/json" \
-w "\n\nHTTP Status: %{http_code}\n" | jq .

# 3. POST Example (allocate seats)
echo ""
echo "📡 Calling API POST /api/v1/cart/allocateBestSeats..."

curl -X POST \
"$API_BASE_URL/api/v1/cart/allocateBestSeats" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "x-tenant: $TENANT" \
-H "Content-Type: application/json" \
-d '{
"sessionId": 67890,
"catalogId": 999,
"quantity": 4,
"priceCategory": "CAT1"
}' \
-w "\n\nHTTP Status: %{http_code}\n" | jq .

Simplified Script for Quick Tests:

# test-api.sh
export TEMPSREEL_CLIENT_SECRET="YourConfidentialSecret123!"

TOKEN=$(curl -s -X POST \
"https://login.microsoftonline.com/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/oauth2/v2.0/token" \
-d "grant_type=client_credentials&client_id=12345678-90ab-cdef-1234-567890abcdef&client_secret=$TEMPSREEL_CLIENT_SECRET&scope=api://a1b2c3d4-e5f6-7890-abcd-ef1234567890/.default" \
| jq -r '.access_token')

curl -H "Authorization: Bearer $TOKEN" \
-H "x-tenant: ffr" \
"https://tempsreel.connect.tickandlive.com/api/v1/catalog/events?startDate=2026-01-01&endDate=2026-12-31"

Python

import os
import requests

def get_access_token():
"""Get OAuth 2.0 token from Azure AD"""
token_url = "https://login.microsoftonline.com/YOUR_TENANT_ID/oauth2/v2.0/token"

token_data = {
'grant_type': 'client_credentials',
'client_id': 'YOUR_CLIENT_ID',
'client_secret': os.getenv('TEMPSREEL_CLIENT_SECRET'),
'scope': 'YOUR_API_SCOPE'
}

response = requests.post(token_url, data=token_data)
response.raise_for_status()
return response.json()['access_token']

def call_api(endpoint, tenant, params=None):
"""Call the TempsRéel API"""
token = get_access_token()

headers = {
'Authorization': f'Bearer {token}',
'x-tenant': tenant,
'Accept': 'application/json'
}

response = requests.get(
f'https://tempsreel.connect.tickandlive.com{endpoint}',
headers=headers,
params=params
)

response.raise_for_status()
return response.json()

# Example usage
if __name__ == '__main__':
events = call_api(
'/api/v1/catalog/events',
tenant='ffr',
params={
'startDate': '2026-01-01',
'endDate': '2026-12-31'
}
)

print(f"Found {len(events.get('events', []))} events")

JavaScript/Node.js

// ⚠️ Server-side only - never expose client_secret in browser
async function getAccessToken() {
const response = await fetch(
"https://login.microsoftonline.com/YOUR_TENANT_ID/oauth2/v2.0/token",
{
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: "YOUR_CLIENT_ID",
client_secret: process.env.TEMPSREEL_CLIENT_SECRET,
scope: "YOUR_API_SCOPE",
}),
}
);
const data = await response.json();
return data.access_token;
}

async function callApi(endpoint, tenant, params = {}) {
const token = await getAccessToken();
const url = new URL(`https://tempsreel.connect.tickandlive.com${endpoint}`);
Object.keys(params).forEach((key) =>
url.searchParams.append(key, params[key])
);

const response = await fetch(url, {
headers: {
Authorization: `Bearer ${token}`,
"x-tenant": tenant,
Accept: "application/json",
},
});

if (!response.ok) {
throw new Error(`API Error: ${response.status}`);
}

return await response.json();
}

// Example usage
(async () => {
const events = await callApi("/api/v1/catalog/events", "ffr", {
startDate: "2026-01-01",
endDate: "2026-12-31",
});

console.log(`Found ${events.events.length} events`);
})();

Error Handling

The API returns errors in RFC 7807 Problem Details format.

HTTP Status Codes

CodeMeaningTypical Cause
400Bad RequestInvalid data in request
401UnauthorizedMissing or invalid token
403ForbiddenAccess denied to tenant/resource
404Not FoundNon-existent resource
422Unprocessable EntityBusiness validation failed
500Internal Server ErrorServer error
503Service UnavailableService temporarily unavailable

Error Response Format

{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.4",
"title": "Forbidden",
"status": 403,
"detail": "Access denied: client 'bc1ebefb-68f4-430e-9459-0b6f442e09ad' is not authorized to access tenant 'ffr'",
"instance": "/api/v1/catalog/events",
"traceId": "00-abc123def456-789-00",
"timestamp": "2026-01-10T14:30:00Z"
}

Common Errors

401 Unauthorized - Azure AD Authentication Problem

Case 1: Missing Token

{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.2",
"title": "Unauthorized",
"status": 401,
"detail": "Authorization header is missing"
}

Case 2: Invalid or Expired Token

{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.2",
"title": "Unauthorized",
"status": 401,
"detail": "The token is invalid or expired"
}

Case 3: Token Acquisition Failure (Azure AD)

{
"error": "invalid_client",
"error_description": "AADSTS7000215: Invalid client secret provided",
"error_codes": [7000215],
"correlation_id": "abc-123-def"
}

Solutions:

  • ✅ Verify the Authorization: Bearer {token} header is present
  • ✅ Verify the clientSecret is correct and hasn't expired
  • ✅ Request a new token if the current one is expired (>1h)
  • ✅ Verify the scope is correct: api://{API_CLIENT_ID}/.default
  • ✅ Verify the App Registration has API.Access permissions with admin consent

403 Forbidden - Authorization Problem

Case 1: Unauthorized Tenant

{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.4",
"title": "Forbidden",
"status": 403,
"detail": "Access denied: client 'bc1ebefb-68f4-430e-9459-0b6f442e09ad' is not authorized to access tenant 'xyz'",
"traceId": "00-abc123def456-789-00"
}

Solutions:

  • ✅ Verify the tenant exists in your authorized tenants list (provided by TickAndLive)
  • ✅ Verify the spelling of the x-tenant header
  • ✅ Contact TickAndLive to request access to a new tenant

💡 Difference 401 vs 403:

  • 401 Unauthorized: Problem with Azure AD token (missing, invalid, expired)
  • 403 Forbidden: Valid token but you don't have permission to access this tenant

422 Unprocessable Entity - Validation Failed

{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.21",
"title": "Validation Failed",
"status": 422,
"errors": {
"startDate": ["The startDate field is required."],
"quantity": ["Quantity must be between 1 and 10."]
}
}

Solutions:

  • Correct the request data according to the error messages

Retry Strategy

For 500 and 503 errors, implement exponential backoff:

int maxRetries = 3;
int delayMs = 1000;

for (int i = 0; i < maxRetries; i++)
{
var response = await client.GetAsync(url);

if (response.IsSuccessStatusCode)
return response;

if (response.StatusCode == HttpStatusCode.InternalServerError ||
response.StatusCode == HttpStatusCode.ServiceUnavailable)
{
await Task.Delay(delayMs * (int)Math.Pow(2, i));
continue;
}

throw new Exception($"Request failed: {response.StatusCode}");
}

Support

Additional Documentation

  • Swagger / OpenAPI: https://tempsreel.connect.tickandlive.com/swagger
  • Postman Collection: Available on request

Contact

For any questions or issues:

Information to Provide When Reporting Issues

To expedite resolution, please provide:

  1. TraceId: Present in the error response
  2. Timestamp: Exact date and time of the error
  3. Tenant: The tenant concerned
  4. Endpoint: The URL called
  5. Complete Request: Headers and body (mask the token)
  6. Complete Response: Status code and body

Example:

TraceId: 00-abc123def456-789-00
Timestamp: 2026-01-10T14:30:00Z
Tenant: ffr
Endpoint: POST /api/v1/cart/allocateBestSeats
Status: 403 Forbidden
Response: {"title": "Forbidden", "detail": "Access denied..."}

API Changelog

Version 1.0 (January 2026)

  • ✨ Azure AD authentication with dedicated middleware
  • ✨ Multi-tenant support with x-tenant header
  • ✨ Catalog, cart, order endpoints
  • ✨ Eventim support (specific endpoints)

Document Written: January 2026
API Version: 1.0
Reference Commit: 77ad497


Appendix: Complete Flow Diagram

graph TB
subgraph "1. Authentication"
A1[Your App] -->|clientId + secret| A2[Azure AD]
A2 -->|JWT Token| A1
end

subgraph "2. API Call"
A1 -->|Authorization: Bearer + x-tenant| B1[API Gateway]
end

subgraph "3. Validation"
B1 --> C1[TenantValidation<br/>Middleware]
C1 -->|Verifies x-tenant| C2{Tenant<br/>exists?}
C2 -->|No| E1[403 Forbidden]
C2 -->|Yes| C3[ConnectInterface<br/>Middleware]
C3 -->|Extracts clientId<br/>from token| C4{Config<br/>found?}
C4 -->|No| E2[403 Forbidden<br/>Access denied]
C4 -->|Yes| C5[Set ConnectContext]
end

subgraph "4. Processing"
C5 --> D1[Endpoint Handler]
D1 --> D2[Business Logic]
D2 --> D3[(Database<br/>filtered by<br/>codeInterface)]
D3 --> D4[Response]
end

D4 -->|200 OK + JSON| A1

style A2 fill:#e1f5ff
style C3 fill:#fff4e1
style C5 fill:#e8f5e9
style E1 fill:#ffcdd2
style E2 fill:#ffcdd2