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:

# 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

VersionStatusSupport End DateNotes
v5Current-Latest features and improvements
v4Deprecated2024-12-31Security fixes only
v3End of Life2023-12-31No 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

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

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

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

// 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

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

// v5 response (direct array)
[
  { "id": "con_123", "email": "[email protected]" },
  { "id": "con_456", "email": "[email protected]" }
]

// 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

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

async function checkApiCompatibility(version) {
  const tests = [
    {
      name: "Authentication",
      endpoint: "/me",
      method: "GET",
    },
    {
      name: "List Contacts",
      endpoint: "/contacts",
      method: "GET",
    },
    {
      name: "Filtering",
      endpoint: "/[email protected]",
      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
  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:

{
  "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:

// ✅ 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:

// 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:

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:

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.