List Customers

The List Customers endpoint provides comprehensive customer retrieval capabilities with advanced filtering, search, and pagination features. This endpoint is designed for building customer management dashboards, implementing search functionality, and generating reports. It returns customers scoped to your application with built-in security and audit logging.

Endpoint DetailsCopied!

  • Method: GET

  • URL: /api/v0/customers

  • Content Type: Not applicable (GET request)

  • Authentication: Required (x-client-key and x-client-secret)

  • Rate Limiting: Subject to standard API rate limits

Request HeadersCopied!

x-client-key: your_client_key_here
x-client-secret: your_client_secret_here

Query ParametersCopied!

Pagination Parameters

Parameter

Type

Required

Default

Description

Constraints

skip

number

0

Number of customers to skip for pagination

Min: 0, Max: 10,000

take

number

10

Number of customers to return per page

Min: 1, Max: 100

Filter Parameters

Parameter

Type

Required

Default

Description

status

CustomerStatus

-

Filter customers by their account status

name

string

-

Search customers by first name or last name

email

string

-

Filter customers by email address

Filter Details

Status Filter Options
  • ACTIVE - Customers with active accounts who can use services

  • BLACKLISTED - Customers who have been blacklisted from services

  • DEACTIVATED - Customers with deactivated accounts

Name Search
  • Case-insensitive partial matching across both first and last names

  • OR logic: Matches if the search term appears in either first_name OR last_name

  • Examples:

    • name=john matches "John Doe", "Jane Johnson", "Michael Johnston"

    • name=smith matches "John Smith", "Sarah Smith", "Smithfield Corp"

Email Filter
  • Case-insensitive partial matching within email addresses

  • Substring search: Finds emails containing the specified text

  • Examples:

    • email=@gmail.com finds all Gmail users

    • email=support finds emails containing "support"

    • email=john finds "john@example.com", "johnny@test.com"

Request ExamplesCopied!

Basic List Request

GET /api/v0/customers HTTP/1.1
Host: api.devdraft.com
x-client-key: your_client_key
x-client-secret: your_client_secre

Paginated Request

GET /api/v0/customers?skip=20&take=50 HTTP/1.1
Host: api.devdraft.com
x-client-key: your_client_key
x-client-secret: your_client_secret

Filtered Request

GET /api/v0/customers?status=ACTIVE&name=john&take=25 HTTP/1.1
Host: api.devdraft.com
x-client-key: your_client_key
x-client-secret: your_client_secret

Search Request

GET /api/v0/customers?email=@business.com&skip=0&take=100 HTTP/1.1
Host: api.devdraft.com
x-client-key: your_client_key
x-client-secret: your_client_secret

Success ResponseCopied!

Status Code: 200 OK

Response Format

Returns an array of customer objects ordered by creation date (newest first).

[
  {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "first_name": "John",
    "last_name": "Doe",
    "email": "john.doe@example.com",
    "phone_number": "+1-555-123-4567",
    "customer_type": "Individual",
    "status": "ACTIVE",
    "last_spent": 250.75,
    "last_purchase_date": "2024-01-10T15:30:00Z",
    "appId": "app_123456789",
    "app_id": "app_123456789",
    "createdAt": "2024-01-01T10:00:00Z",
    "updatedAt": "2024-01-15T14:30:00Z"
  },
  {
    "id": "550e8400-e29b-41d4-a716-446655440001",
    "first_name": "Jane",
    "last_name": "Smith",
    "email": "jane.smith@business.com",
    "phone_number": "+1-555-987-6543",
    "customer_type": "Business",
    "status": "ACTIVE",
    "last_spent": 1500.00,
    "last_purchase_date": "2024-01-20T09:15:00Z",
    "appId": "app_123456789",
    "app_id": "app_123456789",
    "createdAt": "2024-01-05T14:20:00Z",
    "updatedAt": "2024-01-20T09:15:00Z"
  }
]

Empty Result

When no customers match the criteria:

[]

Response Field Descriptions

Field

Type

Description

Nullable

id

string

Unique customer identifier (UUID)

first_name

string

Customer's first name

last_name

string

Customer's last name

email

string

Customer's email address

phone_number

string

Customer's phone number with country code

customer_type

string

Account type (Individual, Business, Enterprise, Non-Profit)

status

string

Account status (ACTIVE, BLACKLISTED, DEACTIVATED)

last_spent

number

Amount of most recent transaction

last_purchase_date

string

ISO 8601 timestamp of last purchase

appId

string

Associated application identifier

app_id

string

Duplicate field for backward compatibility

createdAt

string

ISO 8601 timestamp when customer was created

updatedAt

string

ISO 8601 timestamp when customer was last updated

Error ResponsesCopied!

Authentication Error (401)

{
  "statusCode": 401,
  "message": "Invalid API credentials",
  "error": "Unauthorized"
}

Invalid Parameters (400)

{
  "statusCode": 400,
  "message": [
    "take must not be greater than 100",
    "skip must not be less than 0"
  ],
  "error": "Bad Request"
}

Rate Limit Exceeded (429)

{
  "statusCode": 429,
  "message": "Too many requests",
  "error": "Too Many Requests",
  "retry_after": 60
}

Server Error (500)

{
  "statusCode": 500,
  "message": "Internal server error",
  "error": "Internal Server Error"
}

Integration ExamplesCopied!

cURL Examples

Basic List
curl -X GET https://api.devdraft.com/api/v0/customers \
  -H "x-client-key: your_client_key" \
  -H "x-client-secret: your_client_secret"
With Pagination
curl -X GET "https://api.devdraft.com/api/v0/customers?skip=50&take=25" \
  -H "x-client-key: your_client_key" \
  -H "x-client-secret: your_client_secret"
With Filters
curl -X GET "https://api.devdraft.com/api/v0/customers?status=ACTIVE&name=smith&email=@gmail.com" \
  -H "x-client-key: your_client_key" \
  -H "x-client-secret: your_client_secret"

JavaScript/Node.js Examples

Basic Implementation
async function listCustomers() {
  const response = await fetch('https://api.devdraft.com/api/v0/customers', {
    headers: {
      'x-client-key': process.env.DEVDRAFT_CLIENT_KEY,
      'x-client-secret': process.env.DEVDRAFT_CLIENT_SECRET
    }
  });

  if (!response.ok) {
    throw new Error(`HTTP ${response.status}: ${response.statusText}`);
  }

  const customers = await response.json();
  return customers;
}
Advanced Filtering
async function searchCustomers(filters = {}) {
  const params = new URLSearchParams();
  
  // Add pagination
  if (filters.page) {
    const skip = (filters.page - 1) * (filters.limit || 10);
    params.append('skip', skip.toString());
  }
  
  if (filters.limit) {
    params.append('take', filters.limit.toString());
  }
  
  // Add filters
  if (filters.status) params.append('status', filters.status);
  if (filters.name) params.append('name', filters.name);
  if (filters.email) params.append('email', filters.email);

  const response = await fetch(`https://api.devdraft.com/api/v0/customers?${params}`, {
    headers: {
      'x-client-key': process.env.DEVDRAFT_CLIENT_KEY,
      'x-client-secret': process.env.DEVDRAFT_CLIENT_SECRET
    }
  });

  return await response.json();
}

// Usage examples
const activeCustomers = await searchCustomers({ status: 'ACTIVE', limit: 50 });
const gmailUsers = await searchCustomers({ email: '@gmail.com' });
const johnDoes = await searchCustomers({ name: 'john' });
Pagination Helper
class CustomerPaginator {
  constructor(apiKey, apiSecret) {
    this.apiKey = apiKey;
    this.apiSecret = apiSecret;
    this.baseUrl = 'https://api.devdraft.com/api/v0/customers';
  }

  async getPage(page = 1, limit = 25, filters = {}) {
    const skip = (page - 1) * limit;
    const params = new URLSearchParams({
      skip: skip.toString(),
      take: limit.toString(),
      ...filters
    });

    const response = await fetch(`${this.baseUrl}?${params}`, {
      headers: {
        'x-client-key': this.apiKey,
        'x-client-secret': this.apiSecret
      }
    });

    const customers = await response.json();
    
    return {
      customers,
      page,
      limit,
      hasMore: customers.length === limit
    };
  }

  async *getAllPages(filters = {}, pageSize = 100) {
    let page = 1;
    let hasMore = true;

    while (hasMore) {
      const result = await this.getPage(page, pageSize, filters);
      yield result.customers;
      
      hasMore = result.hasMore;
      page++;
    }
  }
}

// Usage
const paginator = new CustomerPaginator(
  process.env.DEVDRAFT_CLIENT_KEY,
  process.env.DEVDRAFT_CLIENT_SECRET
);

// Get all customers across all pages
for await (const customerBatch of paginator.getAllPages({ status: 'ACTIVE' })) {
  console.log(`Processing ${customerBatch.length} customers`);
  // Process each batch
}

Python Examples

Basic Implementation
import requests
import os
from typing import Optional, Dict, List

class DevdraftCustomers:
    def __init__(self):
        self.base_url = "https://api.devdraft.com/api/v0/customers"
        self.headers = {
            'x-client-key': os.getenv('DEVDRAFT_CLIENT_KEY'),
            'x-client-secret': os.getenv('DEVDRAFT_CLIENT_SECRET')
        }

    def list_customers(
        self, 
        skip: int = 0, 
        take: int = 10,
        status: Optional[str] = None,
        name: Optional[str] = None,
        email: Optional[str] = None
    ) -> List[Dict]:
        """List customers with optional filtering."""
        
        params = {'skip': skip, 'take': take}
        
        if status:
            params['status'] = status
        if name:
            params['name'] = name
        if email:
            params['email'] = email
            
        response = requests.get(
            self.base_url,
            headers=self.headers,
            params=params
        )
        
        response.raise_for_status()
        return response.json()

    def search_customers(self, query: str) -> List[Dict]:
        """Search customers by name or email."""
        # Try name search first
        name_results = self.list_customers(name=query, take=100)
        
        # Then email search
        email_results = self.list_customers(email=query, take=100)
        
        # Combine and deduplicate
        all_results = name_results + email_results
        seen_ids = set()
        unique_results = []
        
        for customer in all_results:
            if customer['id'] not in seen_ids:
                seen_ids.add(customer['id'])
                unique_results.append(customer)
                
        return unique_results

    def get_all_customers(self, **filters) -> List[Dict]:
        """Get all customers with pagination."""
        all_customers = []
        skip = 0
        take = 100  # Max per request
        
        while True:
            batch = self.list_customers(skip=skip, take=take, **filters)
            if not batch:
                break
                
            all_customers.extend(batch)
            skip += take
            
        return all_customers

# Usage examples
client = DevdraftCustomers()

# Get first 50 active customers
active_customers = client.list_customers(status='ACTIVE', take=50)

# Search for customers named "john"
john_customers = client.search_customers('john')

# Get all business customers
business_customers = client.get_all_customers(status='ACTIVE')
Advanced Filtering with Pandas
import pandas as pd

def customers_to_dataframe(customers: List[Dict]) -> pd.DataFrame:
    """Convert customer list to pandas DataFrame for analysis."""
    df = pd.DataFrame(customers)
    
    # Convert timestamps
    df['createdAt'] = pd.to_datetime(df['createdAt'])
    df['updatedAt'] = pd.to_datetime(df['updatedAt'])
    df['last_purchase_date'] = pd.to_datetime(df['last_purchase_date'])
    
    return df

# Get all customers and analyze
client = DevdraftCustomers()
all_customers = client.get_all_customers()
df = customers_to_dataframe(all_customers)

# Analysis examples
print(f"Total customers: {len(df)}")
print(f"Active customers: {len(df[df['status'] == 'ACTIVE'])}")
print(f"Average last spent: ${df['last_spent'].mean():.2f}")

# Find high-value customers
high_value = df[df['last_spent'] > 1000]
print(f"High-value customers: {len(high_value)}")

PHP Examples

Basic Implementation
<?php
class DevdraftCustomers {
    private $baseUrl = 'https://api.devdraft.com/api/v0/customers';
    private $headers;

    public function __construct($clientKey, $clientSecret) {
        $this->headers = [
            'x-client-key: ' . $clientKey,
            'x-client-secret: ' . $clientSecret
        ];
    }

    public function listCustomers($params = []) {
        $url = $this->baseUrl;
        
        if (!empty($params)) {
            $url .= '?' . http_build_query($params);
        }

        $context = stream_context_create([
            'http' => [
                'method' => 'GET',
                'header' => implode("\r\n", $this->headers)
            ]
        ]);

        $result = file_get_contents($url, false, $context);
        
        if ($result === false) {
            throw new Exception('Failed to fetch customers');
        }

        return json_decode($result, true);
    }

    public function searchByStatus($status, $limit = 25) {
        return $this->listCustomers([
            'status' => $status,
            'take' => $limit
        ]);
    }

    public function searchByName($name, $limit = 25) {
        return $this->listCustomers([
            'name' => $name,
            'take' => $limit
        ]);
    }

    public function getAllCustomers($filters = []) {
        $allCustomers = [];
        $skip = 0;
        $take = 100;

        do {
            $params = array_merge($filters, [
                'skip' => $skip,
                'take' => $take
            ]);

            $batch = $this->listCustomers($params);
            $allCustomers = array_merge($allCustomers, $batch);
            $skip += $take;

        } while (count($batch) === $take);

        return $allCustomers;
    }
}

// Usage
$client = new DevdraftCustomers(
    $_ENV['DEVDRAFT_CLIENT_KEY'],
    $_ENV['DEVDRAFT_CLIENT_SECRET']
);

// Get active customers
$activeCustomers = $client->searchByStatus('ACTIVE', 50);

// Search for customers
$johnCustomers = $client->searchByName('john');

// Get all customers (be careful with large datasets)
$allCustomers = $client->getAllCustomers(['status' => 'ACTIVE']);

echo "Found " . count($allCustomers) . " active customers\n";
?>

Advanced Use CasesCopied!

1. Customer Dashboard Implementation

// Real-time dashboard with live updates
class CustomerDashboard {
  constructor(apiKey, apiSecret) {
    this.api = new DevdraftCustomers(apiKey, apiSecret);
    this.refreshInterval = 30000; // 30 seconds
  }

  async loadDashboard() {
    try {
      const [active, blacklisted, deactivated] = await Promise.all([
        this.api.list_customers({ status: 'ACTIVE', take: 100 }),
        this.api.list_customers({ status: 'BLACKLISTED', take: 100 }),
        this.api.list_customers({ status: 'DEACTIVATED', take: 100 })
      ]);

      return {
        active: active.length,
        blacklisted: blacklisted.length,
        deactivated: deactivated.length,
        total: active.length + blacklisted.length + deactivated.length,
        lastUpdated: new Date().toISOString()
      };
    } catch (error) {
      console.error('Dashboard load failed:', error);
      throw error;
    }
  }

  startAutoRefresh(callback) {
    setInterval(async () => {
      try {
        const data = await this.loadDashboard();
        callback(data);
      } catch (error) {
        console.error('Auto refresh failed:', error);
      }
    }, this.refreshInterval);
  }
}

2. Customer Export System

import csv
from datetime import datetime

def export_customers_to_csv(client, filename=None, filters=None):
    """Export customers to CSV file."""
    if filename is None:
        timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
        filename = f'customers_export_{timestamp}.csv'
    
    customers = client.get_all_customers(**(filters or {}))
    
    fieldnames = [
        'id', 'first_name', 'last_name', 'email', 'phone_number',
        'customer_type', 'status', 'last_spent', 'last_purchase_date',
        'createdAt', 'updatedAt'
    ]
    
    with open(filename, 'w', newline='', encoding='utf-8') as csvfile:
        writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
        writer.writeheader()
        
        for customer in customers:
            # Only write the fields we want
            row = {field: customer.get(field, '') for field in fieldnames}
            writer.writerow(row)
    
    return filename, len(customers)

# Usage
client = DevdraftCustomers()
filename, count = export_customers_to_csv(
    client, 
    filters={'status': 'ACTIVE'},
    filename='active_customers.csv'
)
print(f"Exported {count} customers to {filename}")

3. Customer Search Engine

class CustomerSearchEngine {
  constructor(apiClient) {
    this.api = apiClient;
    this.cache = new Map();
    this.cacheTimeout = 5 * 60 * 1000; // 5 minutes
  }

  async search(query, options = {}) {
    const cacheKey = `${query}-${JSON.stringify(options)}`;
    
    // Check cache first
    if (this.cache.has(cacheKey)) {
      const cached = this.cache.get(cacheKey);
      if (Date.now() - cached.timestamp < this.cacheTimeout) {
        return cached.results;
      }
    }

    const results = await this.performSearch(query, options);
    
    // Cache results
    this.cache.set(cacheKey, {
      results,
      timestamp: Date.now()
    });

    return results;
  }

  async performSearch(query, options) {
    const { status, limit = 50 } = options;
    
    // Search by name and email in parallel
    const [nameResults, emailResults] = await Promise.all([
      this.api.searchCustomers({ name: query, status, take: limit }),
      this.api.searchCustomers({ email: query, status, take: limit })
    ]);

    // Combine and deduplicate
    const allResults = [...nameResults, ...emailResults];
    const uniqueResults = Array.from(
      new Map(allResults.map(customer => [customer.id, customer])).values()
    );

    // Sort by relevance (exact matches first)
    return uniqueResults.sort((a, b) => {
      const aExact = this.isExactMatch(a, query);
      const bExact = this.isExactMatch(b, query);
      
      if (aExact && !bExact) return -1;
      if (!aExact && bExact) return 1;
      
      // Then by last purchase date
      const aDate = new Date(a.last_purchase_date || 0);
      const bDate = new Date(b.last_purchase_date || 0);
      return bDate - aDate;
    });
  }

  isExactMatch(customer, query) {
    const q = query.toLowerCase();
    return customer.first_name.toLowerCase() === q ||
           customer.last_name.toLowerCase() === q ||
           customer.email?.toLowerCase() === q;
  }
}

Performance OptimizationCopied!

Caching Strategy

class CachedCustomerAPI {
  constructor(apiClient, cacheOptions = {}) {
    this.api = apiClient;
    this.cache = new Map();
    this.defaultTTL = cacheOptions.ttl || 300000; // 5 minutes
    this.maxCacheSize = cacheOptions.maxSize || 1000;
  }

  async listCustomers(params = {}) {
    const cacheKey = this.getCacheKey(params);
    
    if (this.cache.has(cacheKey)) {
      const cached = this.cache.get(cacheKey);
      if (Date.now() < cached.expiry) {
        return cached.data;
      }
      this.cache.delete(cacheKey);
    }

    const data = await this.api.listCustomers(params);
    
    // Manage cache size
    if (this.cache.size >= this.maxCacheSize) {
      const oldestKey = this.cache.keys().next().value;
      this.cache.delete(oldestKey);
    }

    this.cache.set(cacheKey, {
      data,
      expiry: Date.now() + this.defaultTTL
    });

    return data;
  }

  getCacheKey(params) {
    return JSON.stringify(params, Object.keys(params).sort());
  }

  clearCache() {
    this.cache.clear();
  }
}

Batch Processing

def process_customers_in_batches(client, processor_func, batch_size=100, filters=None):
    """Process all customers in batches to manage memory usage."""
    skip = 0
    processed_count = 0
    
    while True:
        # Fetch batch
        batch = client.list_customers(
            skip=skip,
            take=batch_size,
            **(filters or {})
        )
        
        if not batch:
            break
            
        # Process batch
        try:
            processor_func(batch)
            processed_count += len(batch)
            print(f"Processed {processed_count} customers so far...")
            
        except Exception as e:
            print(f"Error processing batch starting at {skip}: {e}")
            # Optionally continue or break
            
        skip += batch_size
        
        # Rate limiting - avoid overwhelming the API
        time.sleep(0.1)
    
    return processed_count

# Example processor
def send_newsletter(customers):
    for customer in customers:
        if customer.get('email'):
            # Send newsletter logic here
            print(f"Sending newsletter to {customer['email']}")

# Usage
client = DevdraftCustomers()
total_processed = process_customers_in_batches(
    client,
    send_newsletter,
    batch_size=50,
    filters={'status': 'ACTIVE'}
)
print(f"Newsletter sent to {total_processed} customers")

Best PracticesCopied!

1. Efficient Pagination

  • Use take=100 for maximum efficiency when fetching large datasets

  • Implement cursor-based pagination for real-time data

  • Cache results when appropriate to reduce API calls

2. Smart Filtering

  • Combine filters to reduce result sets: status=ACTIVE&email=@business.com

  • Use specific searches rather than broad queries

  • Implement client-side filtering for frequently accessed data

3. Error Handling

  • Always implement retry logic with exponential backoff

  • Handle rate limiting gracefully with proper delays

  • Cache successful responses to reduce API load

4. Security Considerations

  • Never expose API credentials in client-side code

  • Implement proper access controls in your application

  • Log customer access for audit purposes

5. Performance Monitoring

  • Track API response times and implement timeouts

  • Monitor cache hit rates and adjust TTL accordingly

  • Use batch processing for large operations

  • GET /api/v0/customers/{id} - Get specific customer details

  • POST /api/v0/customers - Create new customers

  • PATCH /api/v0/customers/{id} - Update customer information

  • POST /api/v0/customers/{id}/liquidation_addresses - Manage customer liquidation addresses

SupportCopied!

For technical support or questions about listing customers:

  • Verify your pagination parameters are within limits

  • Check filter syntax and supported values

  • Monitor your rate limiting usage

  • Contact support with specific query parameters that cause issues