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
- Introduction
- Prerequisites
- OAuth 2.0 Authentication
- Making API Calls
- Required Headers
- Code Examples
- Error Handling
- 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-tenantheader) - 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
scopeuses the formatapi://{guid}/.defaultwhere the GUID corresponds to the TempsRéel API App Registration (provided by TickAndLive).
⚠️ Security:
- The
clientSecretmust be stored securely (Azure Key Vault, environment variables, etc.) - Never commit the secret to source control
- The
tenantslist 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-tenantheader) - 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):
| Parameter | Value | Description |
|---|---|---|
grant_type | client_credentials | Authentication type (always this value) |
client_id | Provided by TickAndLive | Your Azure AD application identifier |
client_secret | Provided by TickAndLive | Your secret (keep secure) |
scope | Provided by TickAndLive | API access scope Format: api://{guid}/.defaultExample: 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:
| Field | Description |
|---|---|
token_type | Always "Bearer" |
expires_in | Token validity duration in seconds (typically 3599 = ~1h) |
access_token | The JWT token to use for API calls |
Token Lifetime and Renewal
⏱️ Validity Duration: The token is valid for 1 hour (3600 seconds)
Renewal Strategies:
-
Reactive Approach: Wait for 401 error and request a new token
- ❌ Generates additional latency
- ❌ Requires a new request
-
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/eventsPOST /api/v1/cart/allocateBestSeatsPOST /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:
clientSecretis 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
| Code | Meaning | Typical Cause |
|---|---|---|
| 400 | Bad Request | Invalid data in request |
| 401 | Unauthorized | Missing or invalid token |
| 403 | Forbidden | Access denied to tenant/resource |
| 404 | Not Found | Non-existent resource |
| 422 | Unprocessable Entity | Business validation failed |
| 500 | Internal Server Error | Server error |
| 503 | Service Unavailable | Service 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
clientSecretis correct and hasn't expired - ✅ Request a new token if the current one is expired (>1h)
- ✅ Verify the
scopeis correct:api://{API_CLIENT_ID}/.default - ✅ Verify the App Registration has
API.Accesspermissions 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-tenantheader - ✅ 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:
- Support Email: support-api@tickandlive.com
- Hours: Mon-Fri 9am-6pm (CET)
- Emergencies: See partner portal
Information to Provide When Reporting Issues
To expedite resolution, please provide:
- TraceId: Present in the error response
- Timestamp: Exact date and time of the error
- Tenant: The tenant concerned
- Endpoint: The URL called
- Complete Request: Headers and body (mask the token)
- 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-tenantheader - ✨ 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