DepartCart Integration Guide

Quick Integration Options

Option 1: Google Tag Manager (Fastest - 1 day)

Perfect for airlines that want seat selection on confirmation pages with minimal development effort.

What you need:

Setup: 1. Add this code to GTM:

<script>
(function() {
    // Ultra-minimal GTM injectable - just loads the script, no logic
    var script = document.createElement('script');
    script.src = 'https://departcart.com/static/loaders-dist/seat_selection_loader.js';
    script.setAttribute('data-gtm-source', 'true');
    document.head.appendChild(script);
})();
</script>
  1. Configure trigger for confirmation/account pages
  2. Test with sample bookings

Result: Automatic seat selection panels appear on confirmation pages, securely encrypted URLs, fully responsive design.


⭐ This is the recommended approach. Generate seatmap deeplinks through our API using a bearer token. All encryption happens entirely on our servers — you send the raw pnr and last_name, and we return a ready-to-use deeplink. You never handle any encryption keys or secrets beyond your API credentials.

The flow is two steps:

  1. Get a bearer token from POST /generate-bearer-token using your api_key and api_secret.
  2. Generate the deeplink from POST /api/generate-encrypted-seatmap-url, authenticating with the bearer token. The brand is derived from the token, so it is never sent in the request body.

Benefits: - No cryptography to implement: Encryption is handled on our servers; you only send raw passenger data. - Simple credentials: A single api_key / api_secret pair, with short-lived bearer tokens. - Brand inferred from token: No brand field to manage in each request. - Token caching: Reuse a bearer token across many deeplink requests until it expires.

Keep your api_secret server-side. Request bearer tokens from your backend, never from browser or client code.

1. Generate a Bearer Token

Endpoint: POST https://departcart.com/generate-bearer-token

Request body:

{
  "api_key": "YOUR_API_KEY",
  "api_secret": "YOUR_API_SECRET",
  "duration_hours": 24
}

duration_hours is optional and defaults to 24.

Response:

{
  "success": true,
  "bearer_token": "your_brand:...:...:...:...",
  "token_type": "Bearer",
  "expires_in": 86400,
  "expires_at": 1718876400,
  "brand": "your_brand"
}

Cache the bearer_token and reuse it until expires_at.

Endpoint: POST https://departcart.com/api/generate-encrypted-seatmap-url

Headers:

Authorization: Bearer <bearer_token>
Content-Type: application/json

Request body:

{
  "pnr": "ABC123",
  "last_name": "SMITH",
  "source": "api"
}

source is optional and defaults to "cart". Do not send a brand field — the brand is derived from the bearer token.

Response:

{
  "success": true,
  "encrypted_id": "Z0FBQUFB...",
  "seatmap_url": "/seatmap?id=Z0FBQUFB...&source=api",
  "source": "api"
}

seatmap_url is a path on the DepartCart domain. Build the customer-facing deeplink by prefixing the base URL:

https://departcart.com/seatmap?id=Z0FBQUFB...&source=api

3. Code Examples

import requests

BASE_URL = "https://departcart.com"

def get_bearer_token(api_key, api_secret, duration_hours=24):
    """Exchange API credentials for a short-lived bearer token."""
    response = requests.post(f"{BASE_URL}/generate-bearer-token", json={
        "api_key": api_key,
        "api_secret": api_secret,
        "duration_hours": duration_hours
    })
    response.raise_for_status()
    return response.json()["bearer_token"]

def generate_seat_selection_url(bearer_token, pnr, last_name, source="api"):
    """Generate a seatmap deeplink. Encryption happens server-side."""
    response = requests.post(
        f"{BASE_URL}/api/generate-encrypted-seatmap-url",
        headers={
            "Authorization": f"Bearer {bearer_token}",
            "Content-Type": "application/json"
        },
        json={
            "pnr": pnr,
            "last_name": last_name,
            "source": source
        }
    )
    if response.status_code == 200:
        # seatmap_url is a path; prefix with the base URL for the deeplink
        return BASE_URL + response.json()["seatmap_url"]
    return None  # Handle error

# Usage in your booking flow (keep api_secret server-side)
token = get_bearer_token("YOUR_API_KEY", "YOUR_API_SECRET")
seatmap_url = generate_seat_selection_url(token, "ABC123", "SMITH")
if seatmap_url:
    print(f"Seat selection: {seatmap_url}")
const BASE_URL = 'https://departcart.com';

async function getBearerToken(apiKey, apiSecret, durationHours = 24) {
    // Call this from your backend so the api_secret stays server-side
    const response = await fetch(`${BASE_URL}/generate-bearer-token`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
            api_key: apiKey,
            api_secret: apiSecret,
            duration_hours: durationHours
        })
    });
    if (!response.ok) throw new Error('Failed to get bearer token');
    const data = await response.json();
    return data.bearer_token;
}

async function generateSeatSelectionUrl(bearerToken, pnr, lastName, source = 'api') {
    try {
        const response = await fetch(`${BASE_URL}/api/generate-encrypted-seatmap-url`, {
            method: 'POST',
            headers: {
                'Authorization': `Bearer ${bearerToken}`,
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({ pnr, last_name: lastName, source })
        });

        if (response.ok) {
            const data = await response.json();
            // seatmap_url is a path; prefix with the base URL for the deeplink
            return BASE_URL + data.seatmap_url;
        }
        return null; // Handle error
    } catch (error) {
        console.error('Error generating seat selection URL:', error);
        return null;
    }
}

// Usage in your booking flow
const token = await getBearerToken('YOUR_API_KEY', 'YOUR_API_SECRET');
const seatmapUrl = await generateSeatSelectionUrl(token, 'ABC123', 'SMITH');
if (seatmapUrl) {
    console.log(`Seat selection: ${seatmapUrl}`);
}
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;

public class DepartCartClient
{
    private const string BaseUrl = "https://departcart.com";
    private readonly HttpClient _client = new();

    public async Task<string> GetBearerTokenAsync(string apiKey, string apiSecret, int durationHours = 24)
    {
        // Call this from your backend so the api_secret stays server-side
        var payload = new { api_key = apiKey, api_secret = apiSecret, duration_hours = durationHours };
        var content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");

        var response = await _client.PostAsync($"{BaseUrl}/generate-bearer-token", content);
        response.EnsureSuccessStatusCode();

        var result = JsonSerializer.Deserialize<TokenResponse>(await response.Content.ReadAsStringAsync());
        return result.bearer_token;
    }

    public async Task<string> GenerateSeatSelectionUrlAsync(string bearerToken, string pnr, string lastName, string source = "api")
    {
        var payload = new { pnr, last_name = lastName, source };
        var request = new HttpRequestMessage(HttpMethod.Post, $"{BaseUrl}/api/generate-encrypted-seatmap-url")
        {
            Content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json")
        };
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken);

        var response = await _client.SendAsync(request);
        if (response.IsSuccessStatusCode)
        {
            var result = JsonSerializer.Deserialize<SeatmapResponse>(await response.Content.ReadAsStringAsync());
            // seatmap_url is a path; prefix with the base URL for the deeplink
            return BaseUrl + result.seatmap_url;
        }
        return null; // Handle error
    }
}

public class TokenResponse { public string bearer_token { get; set; } }
public class SeatmapResponse { public string seatmap_url { get; set; } }

// Usage in your booking flow
var client = new DepartCartClient();
var token = await client.GetBearerTokenAsync("YOUR_API_KEY", "YOUR_API_SECRET");
var seatmapUrl = await client.GenerateSeatSelectionUrlAsync(token, "ABC123", "SMITH");
if (seatmapUrl != null)
{
    Console.WriteLine($"Seat selection: {seatmapUrl}");
}
require 'net/http'
require 'uri'
require 'json'

class DepartCartAPI
  BASE_URL = 'https://departcart.com'

  def self.get_bearer_token(api_key, api_secret, duration_hours = 24)
    # Call this from your backend so the api_secret stays server-side
    uri = URI("#{BASE_URL}/generate-bearer-token")
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = true

    request = Net::HTTP::Post.new(uri)
    request['Content-Type'] = 'application/json'
    request.body = {
      api_key: api_key,
      api_secret: api_secret,
      duration_hours: duration_hours
    }.to_json

    response = http.request(request)
    raise 'Failed to get bearer token' unless response.code == '200'
    JSON.parse(response.body)['bearer_token']
  end

  def self.generate_seat_selection_url(bearer_token, pnr, last_name, source = 'api')
    uri = URI("#{BASE_URL}/api/generate-encrypted-seatmap-url")
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = true

    request = Net::HTTP::Post.new(uri)
    request['Authorization'] = "Bearer #{bearer_token}"
    request['Content-Type'] = 'application/json'
    request.body = { pnr: pnr, last_name: last_name, source: source }.to_json

    response = http.request(request)
    if response.code == '200'
      # seatmap_url is a path; prefix with the base URL for the deeplink
      BASE_URL + JSON.parse(response.body)['seatmap_url']
    end
  end
end

# Usage in your booking flow
token = DepartCartAPI.get_bearer_token('YOUR_API_KEY', 'YOUR_API_SECRET')
seatmap_url = DepartCartAPI.generate_seat_selection_url(token, 'ABC123', 'SMITH')
puts "Seat selection: #{seatmap_url}" if seatmap_url

If you want more than the seatmap link, call POST https://departcart.com/generate-encrypted-url instead. It uses the same bearer-token auth and { "pnr", "last_name", "source" } body, but returns the seatmap, cart, and checkout deeplinks together as absolute URLs:

{
  "success": true,
  "encrypted_id": "Z0FBQUFB...",
  "seatmap_url": "https://departcart.com/seatmap?id=Z0FBQUFB...&source=cart",
  "cart_url": "https://departcart.com/cart?id=Z0FBQUFB...",
  "checkout_url": "https://departcart.com/checkout?id=Z0FBQUFB...",
  "source": "cart"
}

Use seatmap_url for seat selection, cart_url for the full ancillary cart, or checkout_url to jump straight to payment.

4. Add "Select Seats" Buttons

<!-- In your booking confirmation template -->
<div class="flight-summary">
    <h3>Flight AA1234 - JFK to LAX</h3>
    <p>June 20, 2025 at 8:00 AM</p>

    {% if seatmap_url %}
    <a href="{{ seatmap_url }}" class="btn btn-primary" target="_blank">
        🪑 Select Seats & Add-ons
    </a>
    {% endif %}
</div>

5. Handle Webhook Notifications

from flask import Flask, request, jsonify

@app.route('/webhook/departcart', methods=['POST'])
def handle_ancillary_purchase():
    """Receive real-time notifications when passengers buy ancillaries"""

    webhook_data = request.get_json()

    if webhook_data['event_type'] == 'transaction.completed':
        transaction = webhook_data['transaction']

        # Update your booking system
        update_passenger_ancillaries(
            pnr=transaction['pnr'],
            seats=transaction['ancillaries']
        )

        # Send confirmation email
        send_ancillary_confirmation_email(transaction)

    return jsonify({'status': 'received'})
using Microsoft.AspNetCore.Mvc;
using System.Text.Json;

[ApiController]
[Route("webhook")]
public class WebhookController : ControllerBase
{
    [HttpPost("departcart")]
    public async Task<IActionResult> HandleAncillaryPurchase()
    {
        using var reader = new StreamReader(Request.Body);
        var payload = await reader.ReadToEndAsync();
        var webhookData = JsonSerializer.Deserialize<WebhookPayload>(payload);

        if (webhookData.event_type == "transaction.completed")
        {
            var transaction = webhookData.transaction;

            // Update your booking system
            await UpdatePassengerAncillaries(
                transaction.pnr,
                transaction.ancillaries
            );

            // Send confirmation email
            await SendAncillaryConfirmationEmail(transaction);
        }

        return Ok(new { status = "received" });
    }
}
const express = require('express');
const app = express();

app.use(express.json());

app.post('/webhook/departcart', async (req, res) => {
    try {
        const webhookData = req.body;

        if (webhookData.event_type === 'transaction.completed') {
            const transaction = webhookData.transaction;

            // Update your booking system
            await updatePassengerAncillaries(
                transaction.pnr,
                transaction.ancillaries
            );

            // Send confirmation email
            await sendAncillaryConfirmationEmail(transaction);
        }

        res.json({ status: 'received' });
    } catch (error) {
        console.error('Webhook error:', error);
        res.status(500).json({ error: 'Internal server error' });
    }
});
import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Map;

@RestController
@RequestMapping("/webhook")
public class WebhookController {

    @PostMapping("/departcart")
    public ResponseEntity<Map<String, String>> handleAncillaryPurchase(
            @RequestBody String payload) {

        try {
            ObjectMapper mapper = new ObjectMapper();
            Map<String, Object> webhookData = mapper.readValue(payload, Map.class);
            String eventType = (String) webhookData.get("event_type");

            if ("transaction.completed".equals(eventType)) {
                Map<String, Object> transaction = (Map<String, Object>) webhookData.get("transaction");

                // Update your booking system
                updatePassengerAncillaries(
                    (String) transaction.get("pnr"),
                    (java.util.List<?>) transaction.get("ancillaries")
                );

                // Send confirmation email
                sendAncillaryConfirmationEmail(transaction);
            }

            return ResponseEntity.ok(Map.of("status", "received"));

        } catch (Exception e) {
            return ResponseEntity.status(500)
                .body(Map.of("error", "Internal server error"));
        }
    }

    private void updatePassengerAncillaries(String pnr, java.util.List<?> ancillaries) {
        // Implementation for updating passenger ancillaries
    }

    private void sendAncillaryConfirmationEmail(Map<String, Object> transaction) {
        // Implementation for sending confirmation email
    }
}
require 'sinatra'
require 'json'

class WebhookController < Sinatra::Base

  post '/webhook/departcart' do
    begin
      # Read payload
      request.body.rewind
      payload = request.body.read
      webhook_data = JSON.parse(payload)

      if webhook_data['event_type'] == 'transaction.completed'
        transaction = webhook_data['transaction']

        # Update your booking system
        update_passenger_ancillaries(
          transaction['pnr'],
          transaction['ancillaries']
        )

        # Send confirmation email
        send_ancillary_confirmation_email(transaction)
      end

      content_type :json
      { status: 'received' }.to_json

    rescue => e
      status 500
      { error: 'Internal server error' }.to_json
    end
  end

  private

  def update_passenger_ancillaries(pnr, ancillaries)
    # Implementation for updating passenger ancillaries
  end

  def send_ancillary_confirmation_email(transaction)
    # Implementation for sending confirmation email
  end
end

Integration Comparison

Feature GTM API Integration ⭐
Setup Time 1 day 1-2 days
Development Minimal Low
Customization Limited High
Security High High (server-side encryption)
Performance Good Good
Credentials None API key + secret
Maintenance None Low

What We Provide During Onboarding

Brand Configuration

GDS Integration

Payment Processing

Webhook Setup

Testing Tools


Success Metrics & ROI

Typical Performance

Revenue Examples

For an airline with 10,000 monthly passengers:


Getting Started

  1. Contact Integration Team: integration@departcart.com
  2. Schedule Onboarding Call: 30-minute technical overview
  3. Receive Credentials: API key, API secret, and documentation
  4. Choose Integration: GTM or the bearer-token API integration
  5. Go Live: Testing, validation, and production deployment

Next Steps


Questions? Contact our integration team at integration@departcart.com