> ## Documentation Index
> Fetch the complete documentation index at: https://docs.firstquadrant.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# API versioning

> Understanding API versions, breaking changes, and migration strategies

The FirstQuadrant API uses URL-based versioning to ensure backward compatibility while allowing for continuous improvement. This guide explains our versioning strategy and how to handle version changes.

## Current version

The current API version is **v5**, accessible at:

```
https://api.us.firstquadrant.ai/v5
```

## Version format

### URL versioning

API versions are included in the URL path:

```bash theme={null}
# Current version
https://api.us.firstquadrant.ai/v5/contacts

# Previous versions (deprecated)
https://api.us.firstquadrant.ai/v4/contacts
https://api.us.firstquadrant.ai/v3/contacts
```

### Version header

Every API response includes a `Version` header with detailed version information:

```
Version: firstquadrant.ai-2024-01-15-a1b2c3d4
```

Format: `firstquadrant.ai-YYYY-MM-DD-{commitHash}`

This header provides:

* **Date**: When this version was deployed
* **Commit Hash**: The exact code version running

## Version lifecycle

### Version support policy

| Version | Status      | Support End Date | Notes                            |
| ------- | ----------- | ---------------- | -------------------------------- |
| v5      | **Current** | -                | Latest features and improvements |
| v4      | Deprecated  | 2024-12-31       | Security fixes only              |
| v3      | End of Life | 2023-12-31       | No longer available              |

### Deprecation timeline

1. **Announcement**: 6 months before deprecation
2. **Deprecation**: Version marked as deprecated, security fixes only
3. **End of Life**: 12 months after deprecation announcement
4. **Removal**: API version no longer accessible

## Breaking vs non-breaking changes

### Non-breaking changes (no version change)

These changes are made without incrementing the API version:

* Adding new endpoints
* Adding new optional fields to responses
* Adding new optional parameters to requests
* Adding new values to enums (when the client can handle unknown values)
* Performance improvements
* Bug fixes

### Breaking changes (new version required)

These changes require a new API version:

* Removing endpoints
* Removing or renaming fields
* Changing field types
* Changing authentication methods
* Modifying validation rules
* Changing error response formats
* Removing enum values

## Checking your API version

### Via cURL

```bash theme={null}
curl -I https://api.us.firstquadrant.ai/v5/me \
  -H "Authorization: Bearer YOUR_API_KEY"

# Response headers include:
# Version: firstquadrant.ai-2024-01-15-a1b2c3d4
```

### Programmatically

```javascript theme={null}
const response = await fetch("https://api.us.firstquadrant.ai/v5/me", {
  headers: {
    Authorization: "Bearer YOUR_API_KEY",
  },
});

const version = response.headers.get("Version");
console.log("API Version:", version);
```

## Migration guide

### Preparing for version changes

1. **Monitor Deprecation Notices**: Subscribe to API changelog
2. **Test Early**: Use staging environment to test new versions
3. **Gradual Migration**: Update services incrementally
4. **Version Abstraction**: Implement version handling in your client

### Version abstraction pattern

```javascript theme={null}
class FirstQuadrantClient {
  constructor(config) {
    this.apiKey = config.apiKey;
    this.version = config.version || "v5";
    this.baseUrl = `https://api.us.firstquadrant.ai/${this.version}`;
  }

  async request(endpoint, options = {}) {
    const url = `${this.baseUrl}${endpoint}`;

    const response = await fetch(url, {
      ...options,
      headers: {
        Authorization: `Bearer ${this.apiKey}`,
        "Content-Type": "application/json",
        ...options.headers,
      },
    });

    // Log version for monitoring
    console.log("API Version:", response.headers.get("Version"));

    return response;
  }

  // Version-specific handling
  async getContacts(params) {
    switch (this.version) {
      case "v5":
        return this.getContactsV5(params);
      case "v4":
        return this.getContactsV4(params);
      default:
        throw new Error(`Unsupported API version: ${this.version}`);
    }
  }

  async getContactsV5(params) {
    // v5 implementation with new features
    const queryParams = new URLSearchParams({
      ...params,
      // v5 supports advanced filtering
      "filter.status.equals": params.status,
    });

    return this.request(`/contacts?${queryParams}`);
  }

  async getContactsV4(params) {
    // v4 implementation with compatibility layer
    const queryParams = new URLSearchParams({
      ...params,
      // v4 uses different parameter format
      status: params.status,
    });

    return this.request(`/contacts?${queryParams}`);
  }
}
```

## Version-specific changes

### v5 (Current)

Released: January 2024

**New Features:**

* Advanced filtering with dot notation
* Cursor-based pagination improvements
* Enhanced field selection
* Batch operation support
* Improved error responses

**Breaking Changes from v4:**

* Filter syntax changed from `filter[field][op]` to `filter.field.op`
* Removed deprecated `/legacy` endpoints
* Standardized ID prefixes across all resources

### v4 to v5 migration

#### Filter syntax update

```javascript theme={null}
// v4 syntax
const v4Params = {
  "filter[email][contains]": "@example.com",
  "filter[tags][has]": "customer",
};

// v5 syntax
const v5Params = {
  "filter.email.contains": "@example.com",
  "filter.tags.has": "customer",
};

// Migration helper
function migrateFilters(v4Filters) {
  const v5Filters = {};

  for (const [key, value] of Object.entries(v4Filters)) {
    // Convert filter[field][op] to filter.field.op
    const match = key.match(/^filter\[([^\]]+)\]\[([^\]]+)\]$/);
    if (match) {
      const [, field, op] = match;
      v5Filters[`filter.${field}.${op}`] = value;
    } else {
      v5Filters[key] = value;
    }
  }

  return v5Filters;
}
```

#### Response format changes

```javascript theme={null}
// v4 response wrapper
{
  "success": true,
  "data": [...],
  "meta": {
    "total": 100,
    "page": 1
  }
}

// v5 response (direct array)
[
  { "id": "con_123", "email": "john@example.com" },
  { "id": "con_456", "email": "jane@example.com" }
]

// Migration adapter
function adaptV4Response(v5Response, endpoint) {
  if (endpoint.includes('/count')) {
    return v5Response; // Count endpoint unchanged
  }

  if (Array.isArray(v5Response)) {
    return {
      success: true,
      data: v5Response,
      meta: {
        count: v5Response.length
      }
    };
  }

  return {
    success: true,
    data: v5Response
  };
}
```

## Testing version compatibility

### Version-specific test suite

```javascript theme={null}
describe("API Version Compatibility", () => {
  const versions = ["v4", "v5"];

  versions.forEach((version) => {
    describe(`API ${version}`, () => {
      let client;

      beforeEach(() => {
        client = new FirstQuadrantClient({
          apiKey: process.env.TEST_API_KEY,
          version,
        });
      });

      it("should fetch contacts", async () => {
        const contacts = await client.getContacts({
          status: "active",
        });

        expect(contacts).toBeDefined();
        // Version-specific assertions
        if (version === "v4") {
          expect(contacts).toHaveProperty("data");
        } else {
          expect(Array.isArray(contacts)).toBe(true);
        }
      });
    });
  });
});
```

### Compatibility checker

```javascript theme={null}
async function checkApiCompatibility(version) {
  const tests = [
    {
      name: "Authentication",
      endpoint: "/me",
      method: "GET",
    },
    {
      name: "List Contacts",
      endpoint: "/contacts",
      method: "GET",
    },
    {
      name: "Filtering",
      endpoint: "/contacts?filter.email.contains=@example.com",
      method: "GET",
    },
  ];

  const results = [];

  for (const test of tests) {
    try {
      const response = await fetch(`https://api.us.firstquadrant.ai/${version}${test.endpoint}`, {
        method: test.method,
        headers: {
          Authorization: `Bearer ${API_KEY}`,
        },
      });

      results.push({
        test: test.name,
        status: response.ok ? "PASS" : "FAIL",
        statusCode: response.status,
      });
    } catch (error) {
      results.push({
        test: test.name,
        status: "ERROR",
        error: error.message,
      });
    }
  }

  return results;
}
```

## Staying updated

### API changelog

Monitor changes through:

1. **Changelog**: [docs.firstquadrant.ai/changelog](https://docs.firstquadrant.ai/changelog)
2. **Email Notifications**: Subscribe to API updates
3. **Version Header**: Monitor the `Version` header for changes

### Webhooks for version changes

Subscribe to version change notifications:

```javascript theme={null}
{
  "event": "api.version.deprecated",
  "version": "v4",
  "deprecationDate": "2024-06-01",
  "endOfLifeDate": "2024-12-31",
  "migrationGuide": "https://docs.firstquadrant.ai/migration/v4-to-v5"
}
```

## Best practices

### 1. Explicit version declaration

Always explicitly specify the API version:

```javascript theme={null}
// ✅ Good: Explicit version
const API_VERSION = "v5";
const API_BASE = `https://api.us.firstquadrant.ai/${API_VERSION}`;

// ❌ Bad: Implicit latest version
const API_BASE = "https://api.us.firstquadrant.ai/latest";
```

### 2. Version configuration

Make version configurable:

```javascript theme={null}
// config.js
export const config = {
  api: {
    version: process.env.FIRSTQUADRANT_API_VERSION || "v5",
    key: process.env.FIRSTQUADRANT_API_KEY,
    baseUrl: process.env.FIRSTQUADRANT_API_URL || "https://api.us.firstquadrant.ai",
  },
};
```

### 3. Gradual migration

Implement feature flags for version migration:

```javascript theme={null}
class VersionedClient {
  async getContacts(params) {
    if (featureFlags.useV5Api) {
      return this.v5.getContacts(params);
    } else {
      return this.v4.getContacts(params);
    }
  }
}
```

### 4. Monitor version usage

Track which versions your application uses:

```javascript theme={null}
class APIClient {
  constructor() {
    this.metrics = {
      versionUsage: {},
    };
  }

  async request(version, endpoint) {
    // Track version usage
    this.metrics.versionUsage[version] = (this.metrics.versionUsage[version] || 0) + 1;

    // Make request
    const response = await fetch(`https://api.us.firstquadrant.ai/${version}${endpoint}`);

    // Log version from response
    console.log(`Used API ${version}, Server: ${response.headers.get("Version")}`);

    return response;
  }
}
```

## Summary

* **Current Version**: v5
* **Version in URL**: `https://api.us.firstquadrant.ai/v5`
* **Version Header**: Included in all responses
* **Support Period**: 12+ months after deprecation
* **Migration Notice**: 6 months before changes
* **Best Practice**: Always use explicit versioning

Stay informed about version changes and plan migrations early to ensure smooth transitions between API versions.
