# Partner Encryption Guide

URL: https://all-ancillaries-9z82vkas5-britoncos-projects.vercel.app/docs/partner-encryption-guide
Updated: 2026-06-25

Security and encryption requirements for partner implementations.

# Partner Integration Guide

## Overview

DepartCart encrypts PNR and passenger last name data in seatmap URLs to protect sensitive passenger information. **Partners do not perform any encryption themselves.** Instead you authenticate with an API key and secret to obtain a short-lived bearer token, then call our API with the raw booking details (`pnr` and `last_name`). DepartCart encrypts the data server-side and returns a ready-to-use seatmap deeplink:

```
https://departcart.com/seatmap?id=Z0FBQUFBQm9VQTE2TnFpUk1Uc0lfd1lyX3VLcUU2aEN5UndZUnp3RS1ma2UxM1hQZjg4Z3ZILWQtOWNxZVBhTzFING5IaUdyZ2ZmRjhwaDR0WHRVWERZRGVNYVlWbVBXUHJTVVlUMmZsR3VoU3RCdjBVNnRHcWZ3SWJSOFFFM28ycjNCN3BZbXA5Tjd1SkxuSTd2cDhadGN5Sm14OTU1blh3PT0=&source=cart
```

> **Last name validation:** When a passenger opens the link, DepartCart validates that the last name in the encrypted URL matches **any** passenger on the booking, not just the lead traveller. You can safely use any passenger's last name from the PNR when generating the link.

## Credentials

Each partner brand is issued an **API key** and **API secret** during onboarding. These are used to obtain bearer tokens. Encryption keys, passwords, and salts are managed entirely by DepartCart and are never shared with partners.

| Credential | Description |
|------------|-------------|
| API key | Public identifier for your brand. Sent when requesting a bearer token. |
| API secret | Confidential. Used to authenticate token requests. Never expose it in client-side code. |

## Step 1. Request a bearer token

**Endpoint**: `POST /generate-bearer-token`

**Request Body**:
```json
{
  "api_key": "YOUR_API_KEY",
  "api_secret": "YOUR_API_SECRET",
  "duration_hours": 24
}
```

`duration_hours` is optional and defaults to `24`.

**Response**:
```json
{
  "success": true,
  "bearer_token": "your_brand:1718790000:1718876400:Ab12Cd...:9f8e7d...",
  "token_type": "Bearer",
  "expires_in": 86400,
  "expires_at": 1718876400,
  "brand": "your_brand"
}
```

Cache and reuse the token until it is close to expiry rather than requesting a new one on every call. A `401` response means the token is missing, invalid, or expired.

## Step 2. Generate a seatmap deeplink

**Endpoint**: `POST /api/generate-encrypted-seatmap-url`

**Headers**:
```text
Authorization: Bearer <token>
Content-Type: application/json
```

**Request Body**:

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `pnr` | string | Yes | Booking reference / PNR (e.g. `ABC123`). |
| `last_name` | string | Yes | Any passenger's last name from the booking (e.g. `SMITH`). |
| `source` | string | No | Integration source identifier (e.g. `api`, `app`). Defaults to `cart`. |

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

**Response**:
```json
{
  "success": true,
  "encrypted_id": "Z0FBQUFBQm9VQTE2...",
  "seatmap_url": "/seatmap?id=Z0FBQUFBQm9VQTE2...&source=api",
  "source": "api"
}
```

`seatmap_url` is a path on the DepartCart domain. Prepend `https://departcart.com` to build the full customer-facing deeplink.

## Generate All Deeplinks

Generate the **seatmap, cart, and checkout** deeplinks for a booking in a single call. As with the seatmap endpoint, encryption is performed server-side and the brand is taken from your bearer token. The difference is that this endpoint returns all three deeplinks as **absolute URLs**.

**Endpoint**: `POST /generate-encrypted-url`

**Headers**:
```text
Authorization: Bearer <token>
Content-Type: application/json
```

**Request Body**:

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `pnr` | string | Yes | Booking reference / PNR. |
| `last_name` | string | Yes | Any passenger's last name from the booking. |
| `source` | string | No | Integration source identifier. Defaults to `cart`. |

```json
{
  "pnr": "ABC123",
  "last_name": "SMITH",
  "source": "cart"
}
```

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

Use `seatmap_url` to drop the customer directly into seat selection, `cart_url` for the full ancillary cart, or `checkout_url` to jump straight to payment. These are absolute URLs and can be used as-is.

---

## Implementation Examples

### Python

```python
import requests

BASE_URL = "https://departcart.com"

# 1) Exchange API credentials for a bearer token
auth = requests.post(f"{BASE_URL}/generate-bearer-token", json={
    "api_key": "YOUR_API_KEY",
    "api_secret": "YOUR_API_SECRET",
}).json()
token = auth["bearer_token"]

# 2) Generate the seatmap deeplink for a booking
resp = requests.post(
    f"{BASE_URL}/api/generate-encrypted-seatmap-url",
    headers={"Authorization": f"Bearer {token}"},
    json={"pnr": "ABC123", "last_name": "SMITH", "source": "api"},
).json()

seatmap_deeplink = f"{BASE_URL}{resp['seatmap_url']}"
print(seatmap_deeplink)
```

### C#

```csharp
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Http.Json;

const string baseUrl = "https://departcart.com";
var client = new HttpClient();

// 1) Exchange API credentials for a bearer token
var authResp = await client.PostAsJsonAsync($"{baseUrl}/generate-bearer-token", new
{
    api_key = "YOUR_API_KEY",
    api_secret = "YOUR_API_SECRET",
});
var auth = await authResp.Content.ReadFromJsonAsync<Dictionary<string, object>>();
string token = auth["bearer_token"].ToString();

// 2) Generate the seatmap deeplink for a booking
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var resp = await client.PostAsJsonAsync($"{baseUrl}/api/generate-encrypted-seatmap-url", new
{
    pnr = "ABC123",
    last_name = "SMITH",
    source = "api",
});
var result = await resp.Content.ReadFromJsonAsync<Dictionary<string, object>>();
string seatmapDeeplink = $"{baseUrl}{result["seatmap_url"]}";
```

### JavaScript / Node.js

```javascript
const BASE_URL = 'https://departcart.com';

// 1) Exchange API credentials for a bearer token
const auth = await fetch(`${BASE_URL}/generate-bearer-token`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
        api_key: 'YOUR_API_KEY',
        api_secret: 'YOUR_API_SECRET',
    }),
}).then(r => r.json());

// 2) Generate the seatmap deeplink for a booking
const result = await fetch(`${BASE_URL}/api/generate-encrypted-seatmap-url`, {
    method: 'POST',
    headers: {
        'Authorization': `Bearer ${auth.bearer_token}`,
        'Content-Type': 'application/json',
    },
    body: JSON.stringify({ pnr: 'ABC123', last_name: 'SMITH', source: 'api' }),
}).then(r => r.json());

const seatmapDeeplink = `${BASE_URL}${result.seatmap_url}`;
console.log(seatmapDeeplink);
```

> **Keep your API secret server-side.** Bearer tokens should be requested from your backend. Never embed your API secret in browser or mobile client code.

## Testing

1. Request a bearer token with your sandbox credentials.
2. Call `/api/generate-encrypted-seatmap-url` with a sample `pnr` and `last_name`.
3. Open `https://departcart.com{seatmap_url}` and confirm the seatmap loads correctly.

## Security Benefits

1. **Data Protection**: PNR and passenger names are encrypted in transit and within the URL.
2. **No shared secrets**: Partners never hold encryption keys; only DepartCart can encrypt or decrypt.
3. **Brand Isolation**: Each brand's data is encrypted with its own server-side key.
4. **Short-lived access**: Bearer tokens expire, limiting the impact of a leaked token.

## Support

For implementation assistance or to receive your brand's API credentials, contact:
- Technical Support: [support@departcart.com](mailto:support@departcart.com)
- Integration Team: [integration@departcart.com](mailto:integration@departcart.com)

## FAQ

**Q: Do I need to implement encryption myself?**
A: No. Encryption is performed entirely server-side. You send the raw `pnr` and `last_name`; DepartCart returns the encrypted deeplink.

**Q: How long are bearer tokens valid?**
A: By default 24 hours, configurable via `duration_hours` when requesting a token. Request a new token when the current one expires.

**Q: How long are the generated seatmap URLs valid?**
A: The encrypted `id` does not expire, but it is tied to the booking and brand it was generated for.

**Q: Which last name should I send?**
A: Any passenger's last name from the booking. DepartCart validates against all passengers on the PNR.

**Q: What if a customer opens an invalid link?**
A: They will see an "Invalid or expired link" error message.
